字符串
主要包括String、StringBuffer和StringBuilder基本概念
String相关知识
String的基本概念
1、String是被final修饰的类,不能被继承;
2、String实现了Serializable和Comparable接口,并且String支持序列化和可以比较大小;
3、String底层是通过char类型数组的数据实现的,并且被final修饰,所以字符串的值创建之后不可以被修饰,具有不可变性。
当String字符串重新赋值或进行拼接时,不会在原来的内存地址上进行修改,而是重新分配新的内存地址进行赋值。
String的常用方法
方法 | 描述 |
---|---|
int length() | 返回字符串的长度 |
char charAt(int index) | 返回指定索引的字符 |
boolean isEmpty() | 判断字符串是否为空 |
int indexOf(String str) | 返回指定子字符串在当前字符串中第一次出现处的索引 |
int lastIndexOf(String str) | 返回指定子字符串在当前字符串中最后出现处的索引 |
String replace(char older,char newchar) | 替换当前字符串中指定的子字符串 |
String[] split(String regex) | 根据指定的符号拆分当前字符串,然后返回一个String数组 |
boolean contains(CharSequence s) | 判断当前字符串中是否包含指定的字符串 |
String substring(int beginIndex) | 返回从beginIndex到末尾的子字符串 |
String substring(int beginIndex,int endIndex) | 返回从beginIndex到endIndex前一位的子字符串 |
String和char[ ]之间相互转换
String转化为char[ ] 调用String的toCharArray方法
String s1 = "ABCD";
char[ ] chars = s1.toCharArray();
char[]转化为String 调用String的构造器
char[] chars = new char[]{'A','B','C','D'};
String s = new String(chars);
====================================
String和byte[]之间相互转换
String转化为byte[ ] 调用String的getBytes方法
String str = 'ABCD';
byte[ ] bytes = str.getBytes();
byte[ ]转化为String 调用String的构造器
byte[ ] bytes = new byte[ ]{'A','B','1','2'};
String str = new String(bytes);//也可以使用指定的字符编码
StringBuffer
基本概念
1、StringBuffer代表可变的字符序列,很多方法与String相同,作为参数传递时,方法内部可以改变值。
2、底层char类型数组没有被final修饰,可以不断扩容。通过count记录有效字符的个数。
3、StringBuffer类不同于String,其对象必须使用构造器生成。共有三个构造器。
StringBuffer构造器
共有三个构造器
①StringBuffer():初始容量为16的字符串缓冲区
②StringBuffer(int size):构造指定容量的字符串缓冲区
③StringBuffer(String str):将内容初始化为指定字符串内容
StirngBuffer类的常用方法
append(xxx)、delete(int start,int end)、replace(int start,int end,String str)、insert(int offset,xxx)、reverse()
StringBuilder
基本概念
StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且提供的方法也类似。
对比String、StringBuffer和StringBuilder
String:不可变字符序列
StringBuffer:可变字符序列、效率低、线程安全
StringBuilder:可变字符序列、效率高、线程不安全
注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改版其值。
抽象类与接口
抽象类(abstract class)
类与类之间具有共同特征,将这些共同特征提取出来,就形成了抽象类。
接口(interface)
接口可看作特殊的抽象类(接口是完全抽象,抽象类是半抽象)。接口也是一种“引用数据类型”,编译后也是一个 .class 字节码文件。
注意:接口和抽象类都不能实例化。
抽象类与接口的区别
①成员上的区别
类型 | 构造方法 | 成员变量 | 方法 |
---|---|---|---|
接口 | 有 | 变量和常量均可 | 可以是抽象方法也可以是非抽象方法 |
抽象类 | 无 | 只能是public static final修饰的常量 | JDK1.8之前,只有抽象方法;1.8开始,新增默认方法和静态方法 |
②关系上的区别
类与类:只能单继承,但是可以多重继承。
类与接口:单实现和多实现均可。
接口和接口:可以单继承也可以多继承。
③理念的不同
抽象类是用来继承的,继承使用extends关键字,是一种is-a的关系。例如:丁真是一个理塘人。
接口是用来实现的,实现所用的关键字是implements关键字,是一种实现关系。例如:手机实现了USB接口的功能。
集合
集合与数组的区别
类型 | 长度区别 | 内容区别 | 元素区别 |
---|---|---|---|
集合 | 长度可变 | 只能是引用类型 | 可以存储不同类型(一般存储同种类型) |
数组 | 长度固定 | 可以是基本类型也可以是引用类型 | 只能存储同一种类型 |
List和Set的区别
类型 | 有序性 | 唯一性 | 获取元素 |
---|---|---|---|
List | 保证按插入顺序排序 | 插入元素可以重复 | 可以根据索引快速获得元素 |
Set | 存储顺序和插入顺序不一致 | 插入元素不能重复 | 不能根据索引快速获得元素 |
Collection集合的方法
方法 | 描述 |
---|---|
boolean add(E e) | 在集合末尾添加元素 |
boolean remove(Object o) | 若本类集中有值和o的值相等的元素,则删除该元素,并返回true |
void clear() | 清除本类集中所有元素,调用完该方法后本类集将为空 |
boolean contains(Object o) | 判断集合中是否包含某元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 返回集合中元素个数 |
boolean addAll(Collection c) | 将一个类集c中的所有元素添加到另一个类集 |
Object[ ] toArray() | 返回一个包含了本类集中所有元素的数组,数组类型为Object[ ] |
Iterator iterator() | 集合的专用遍历方式 |
常用集合法的分类
Collection 接口的接口 对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,可重复
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 使用hash表(数组)存储元素
│————————└ LinkedHashSet 链表维护元素的插入次序
└ —————-TreeSet 底层实现为二叉树,元素排好序
Map 接口 键值对的集合 (双列集合)
├———Hashtable 接口实现类, 同步, 线程安全
├———HashMap(重点) 接口实现类 ,没有同步, 线程不安全
│—————–├ LinkedHashMap 双向链表和哈希表实现
│—————–└ WeakHashMap
├ ——–TreeMap 红黑树对所有的key进行排序
└———IdentifyHashMap
ArrayList和LinkedList相关概念
ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素。
Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素。
LinkedList:底层数据结构是链表,增删快,查询慢,线程不安全,效率高,可以存放重复元素。
HashSet和LinkedHashSet相关概念
HashSet:底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
LinkedHashSet:底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。
Map相关概念
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。
Map 接口提供 3 种集合的视图, Map 的内容可以被当作一组 key 集合,一组 value 集合,或者一组 key-value 映射。
Map下的类主要有:①HashMap ②LinkedHashMap ③HashTable ④TreeMap
Map下最重要的实现类HashMap
HashMap即是采用了链地址法,也就是数组+链表的方式。数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。HashMap中的链表出现越少,性能才会越好。
HashMap有4个构造器,其他构造器如果用户没有传入initialCapacity 和loadFactor这两个参数,会使用默认值
initialCapacity(初始容量)默认为16,loadFactory(加载因子)默认为0.75。
HashMap的扩容机制
当发生哈希冲突并且size大于阈值的时候,需要进行数组扩容,扩容时,需要新建一个长度为之前数组2倍的新的数组,然后将当前的Entry数组中的元素全部传输过去,扩容后的新数组长度为之前的2倍,所以扩容相对来说是个耗资源的操作。
HashMap的底层数据结构
JDK1.7之前 数组+链表
JDK1.7之后 数组+链表+红黑树
链表转红黑树的条件: 元素个数>=64个 链表的长度>=8
红黑树转链表: 红黑树的节点个数<=6个
多线程
基本概念
在java中,如果要实现多线程,就必须依靠线程主体类,而java.lang.Thread是java中负责多线程操作类,只需继承Thread类,就能成为线程主体类,为满足一些特殊要求,也可以通过实现Runnable接口或者Callable接口或使用线程池来完成定义。
Thread类是Runable接口的子类,通过直接覆写Thread类的run方法实际上依然是覆写Runnable接口内的run方法,其实本质上是没有区别的,但是利用Runnable方案实现更加能体现面向对象思维。
使用Runnable接口实现的多线程可以避免单继承的局限,但是还有一个问题就是run方法没有返回值,为了解决这个问题,所以提供了一个Callable接口java.util.concurrent.Callable,Callable有返回值而且可以抛异常。
线程状态转换以及常用方法
新建–》就绪 start()
就绪–》运行 程序员无法控制,全权由cpu自己控制
运行–》阻塞 一个线程获取到锁,其他线程就处于阻塞状态,等线程运行完释放锁后,其他线程从阻塞状态进入就绪状态再竞争cpu
运行–》时间等待 sleep(1000) , wait(1000),join(1000) ,超过时间后会从时间等到回到就绪状态
运行–》等待状态 wait() join() wait等待notify(),join()等待加入的线程执行完毕,从等待状态–》就绪状态
运行–》结束 线程执行完毕,从运行状态转到结束
sleep()和wait()的区别?
sleep()进入时间等待状态,wait()进入等待状态
sleep()不会释放锁,而wait()会释放锁,通过notify进行协作
线程安全及同步
解决数据共享问题必须使用同步,所谓的同步就是指多个线程在同一个时间段内只能有一个线程执行指定的代码,其他线程要等待此线程完成之后才可以继续进行执行,在Java中提供有synchronized关键字以实现同步处理,同步的关键是要为代码加上“锁”。
在java中,线程同步有三种方式:①同步代码块 ②同步方法 ③Lock实现
线程死锁
所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
死锁如何解决?
(1)尽量不要用相互嵌套锁的方式
(2)可以设置超时时间,但获取锁超过一定的时间让线程释放锁
守护线程
守护线程就是伴随主线程,主线程退出,守护线程会自动跟着退出
线程池
使用线程池的好处:
①由于线程的创建和销毁都需要耗费cpu和内存的资源,使用线程池提前创建好节约创建和消耗的时间,避免频繁创建删除线程。使用线程池是一种空间换时间的策略,可以重复利用线程池的线程。
②线程池可以控制线程的数量,避免程序员无节制创建线程导致OOM(内存溢出)。
线程池 ThreadPoolExecutor类
ThreadPoolExecutor自定义线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, //(可选)
RejectedExecutionHandler handler) //(可选)
可以看到,其需要如下几个参数:
corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。
线程池的工作流程
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 线程执行的任务
}
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
为什么先放入队列而不是创建非核心线程执行?
为了寻求一个空间和时间的一个平衡,最大化利用线程池又不浪费时间和空间
Executors已经为我们封装好了 4 种常见的功能线程池:
定长线程池(FixedThreadPool)
定时线程池(ScheduledThreadPool )
可缓存线程池(CachedThreadPool)
单线程化线程池(SingleThreadExecutor)
注意:Executors 的 4 个功能线程池虽然方便,但现在已经不建议使用了,而是建议直接通过使用 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
volatile关键字
volatile是Java虚拟机提供的轻量级的同步机制,它有3个特性:
- 保证可见性
- 不保证原子性
- 禁止指令重排
①保证内存可见性:每个线程会有自己的工作内存,多线程环境下,可能会出现工作内存的值与主内存值不一致造成数据安全的问题,可以通过volatile关键字强制保证工作内存的值与主内存保持同步
②禁止指令重排:重排序是指编译器和处理器为了优化程序性能而对指令进行排序一种方式。由于JVM会指令的执行顺序进行优化,所以有可能会造成指令重排,有些时候没有问题,但有些时候可能会产生问题,可以使用volatile进行禁止指令重排
③不保证原子性:原子性是指不可分隔,完整性,即某个线程正在做某个业务时,中间不能被分割。要么同时成功,要么同时失败。 volatile不具备原子性
锁
了解一些常见的锁的概念
乐观锁和悲观锁
乐观锁: 每次在读取数据的时候都认为其他的人不会去修改该数据,所以不上锁,CAS
悲观锁: 每次读取数据的时候都认为别人会修改数据,所以每次都上锁,其他人都不能访问该数据
公平锁和非公平锁
在多个线程竞争的时候,主要看线程的执行顺序
公平锁: 根据先来后到的方式进行访问,有一定的顺序,轮流访问
非公平锁: 谁抢到的谁用,不一定有固定的顺序 synchronized非公平锁
可重入锁和非重入锁
可重入锁: 当线程获取到锁后可以继续获取该锁,synchronized和ReentrantLock
非可重入锁:线程不能重复获取该锁,使用很少
锁的概念实在太多了,暂时先了解这么多。
IO流
File文件类
封装一个磁盘路径字符串,对这个路径可以执行一次操作
可以封装文件路径、文件夹路径、不存在的路径
功能:
①获取文件、文件夹属性。像是是否为文件,绝对路径等
②创建和删除文件
③返回当前文件夹列表,返回File[ ] 或者 String [ ]
注意:File类不能修改文件内部信息。
IO输入输出流
IO流体系
按方向分:
- 输入流 InputStream Reader 从外设读入内存的过程就是输入
- 输出流 OutputStream Writer 把内存中的数据写到外设中过程就是输出
读入写出,都是相对内存(程序执行在内存中)来说
按内容分:
- 字节流 InputStream OutputStream 传输的单位是一个byte一个byte的传,二进制文件视频,音频,图片
- 字符流 Reader Writer 传输的单位是一个char一个char的传(2个byte),文本文件
序列化的概念
序列化的实现很简单,就是实现Serializable这个接口即可
为什么要实现序列化?
网络传输也好,还是内存数据存入硬盘等对象都不能直接传输,必须先转化成二进制数才能进行传输
对象 - > 二进制数的操作就是 序列化
二进制数 -> 对象 反序列化
网络编程
InetAddress类
InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取
- public static InetAddress getLocalHost()
- public static InetAddress getByName(String host)
Socket
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
套接字分类:
流套接字(stream socket):使用TCP提供可依赖的字节流服务
数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
BIO NIO AIO的区别?
- BIO block io 同步阻塞IO
- NIO non-block io 同步非阻塞IO
- AIO asynchronous io 异步非阻塞IO
同步和异步的区别?
同步:发送一个请求,等待返回后再发送下一个请求,同步可以避免出现死锁,脏读等问题,有执行顺序
异步:发送一个请求,不需要等待返回,直接可以再发送下一个请求,可以提高,保证并发
同步和异步关注点在于消息的通讯机制
阻塞和非阻塞程序再等待调用结果时(消息,返回值)的状态