常用类
1.枚举类
如何设计枚举类
1.所有的属性都是public static final
2.构造器是私有的
3.默认属性调用的是私有的构造器
4.所有的属性通过,
分割
2.String
类的声明:
1.实现了Serializable序列化接口,将对象可以写入到某个指定的地方去
2.实现Comparable接口,可以进行比较
3.CharSequence字符序列都需要实现这个接口
4.Constable,ConstantDesc jdk9添加的两个接口
String类字面值时和new对象时的区别
StringBuffer、StringBuilder、String的区别
String:不可变的字符串序列,底层使用char数组存储
StringBuffer:可变的字符串序列,线程安全,效率低,底层使用char数组存储
StringBuilder:可变的字符串序列,线程不安全,效率高,底层使用char数组存储
效率:StringBuilder>StringBuffer>String
3.Integer
Integer中缓存了-128~127之间的数据,如果通过自动装箱创建的对象在这个范围内,那么此时创建的对象的地址值是相同的,反之不同
4.BigDecima
使用BigDecima对象的创建统一不适用通过double值 使用字符串方式创建 可以减少精确度的损失
IO流
1.流的分类:
方向:输入、输出(流)
单位:字节、字符
处理方式:节点、处理
2.需要记住的一张图
3.对象流
如果一个自定义类需要被序列化或反序列化需要做如下的事情:
-
实现Serializable
-
如果有不希望
反序列化
的属性,需要在属性前面加上transient -
如果不希望因为序列化版本不一致导致程序错误,可以在自定义类中添加属性:
protectde static fianl long serializableUID = 1L;
4.流的执行步骤
1.创建流
2.读取、写出
3.操作
4.关闭
5.注意
刷新(flush())针对于写出数据(手动刷新),打印流
网络编程
网络目的
连接多个计算机做互通。(数据交换)局域网,城域网,广域网
网络的三大基石
ip,端口,协议
socket编程
TCP特点:
三次握手,四次挥手
需要创建连接,握手操作(三次)
安全
传送数据多
1.创建对象
2.获取流
3.发送数据
4.关闭
5.创建对象
6.获取客户端对象
7.获取流
8.关闭
UDP特点:
不需要创建连接
不安全
效率高
传送的数据大小有限制
1.创建对象
2.发送数据包
3.关闭
4.创建对象
5.接收数据包
6.关闭
使用API的步骤
1: 找到指定可以转换json的组件 gson(谷歌) jackson() fastjson(阿里)
2: 下载jar包
3: 找资料、写Demo
集合
一.Collection
list
1.ArrayList
底层使用数据存储
优点
:查找的效率高
缺点
:添加和删除的效率低
三种遍历方式:
1.普通for循环
2.增强for循环
3.Iterator迭代器
2.Queue
3.LinkedList
LinkedList的几个方法:add(),remove(),get | offer(),poll(),peek()
LinkedList的添加逻辑:
last.next = new Node(last,e,null);
1.找到待添加元素的前一个元素
2.获取cur的下一个元素
Node next = cur.next();
3.将它的next指向新元素
cur.next = new Node(cur,5,null);
4.将原先cur的下一个元素的前驱指向待添加的元素
next.pre = cur.next
5.将待添加的元素的next重新指向
new Node().next = next;
注意:hasNext:没有指针下移操作,只是判断是否存在下一个元素 next:指针下移,返回该指针所指向的元素
set
1.HashSet
如果hash表中(hashset、hashmap) 存储自定义对象的时候,很多时候我们说对象的属性一致我们
认为他们是同一个对象,此时需要保证当前自定义类型中必须要重写hashcode和equals方法。
2.TreeSet
底层是红黑树
TreeSet对自定义类进行排序的时候需要进行实现Comparable接口,并重写compareTo
二.Map
问题:compareTo方法返回-1,0,1是如何排序的
1、HashMap
请说明hash表是如何存储元素的? 这样存储有啥问题? 如何解决?它还有问题吗?
jdk8之后的hashmap存储的时候:默认hash中数组的长度是16,加载因子是(0.75)[resize的百分比,扩
容时机]。存储元素时按照key进行存储,首先计算key的hashcode值然后通过hash();方法计算当前
的hash值,然后通过 [length-1&hash] 这种方式进行计算。计算的出来的索引就是当前该元素在数组中
的位置,然后去进行填充,在填充的时候如果数组的某个位置上的链表元素超过个8个,此时在该位置将
链表转换为对应的红黑树。如果当前位置的时null则填充该元素,如果不为null,通过equals方法进行比
较。(线程不安全的)
2、TreeMap
Lambda表达式
一、Lambda表达式和匿名内部类
1.先定义一个接口,声明抽象方法
2.由于该方法需要被重写并调用,所以在测试类中先声明了一个静态调用方法
3.本次测试主要目的是测试通过匿名类重写内部类和匿名实现函内部类数式接口通过lambda表达式重写方法,所以先调用方法,再 考虑重写
二、Lambda表达式的几种类型
无参无返回值
无参有返回值
有参无返回值
有参有返回值
常见函数式接口:Supplier、Consumer、Predicate
Stream
1.创建
List<String> ls = new ArrayList();
Stream<String> stream = ls.stream();
2.中间操作
stream = stream.filter((x)->{return x.getAge() > 20;}).sorted((x,y)->{return x.getAge() - y.getAge();});
stream.forEach(System.out::println);
3.终止
stream.forEach(System.out::println);
线程
线程的创建过程
1.继承Thread类
2.实现Runnable接口
3.设置线程的优先级
4.通过Synchronized序列化线程
实现序列化的方式:
1.在方法声明中写入sychronized,默认使用当前对象作为锁
2.在方法体重声明synchronized(this/xxx.class){},可以使用当前对象或其他对象作为锁,使用当前对象只能锁住当前对 象
Volatile:
可见性:
1.指的是线程之间的可见性,一个线程修改的状态对另一个线程是可见的;
2.volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存;
3.volatile只能让被他修饰的内容具有可见性,不能保证它具有原子性;例如:
volatile int a = 0;
//之后有个操作a++,这个变量具有可见性,但是a++依然是个非原子操作,也就是这个操作同样会存在线程安全问题
原子性:
1.原子是最小的单位,具有不可分割性;例如:
a++这个操作,这个操作实际是a = a + 1;是可分割的;
2.非原子操作都会存在线程安全问题,所以我们需要使用同步(synchronized)把他们变为原子操作;
3.一个操作是原子操作,我们称它具有原子性
4.Java中,synchronized、lock和unlock中操作保证了原子性
有序性:
1.Java中提供了volatile和synchronized两个关键字保证了线程之间操作的有序性,volatile是因为其本身包含“禁止指令重排序”的语义,synchronized是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,此规则决定了持有同一个对象锁的同步块只能串行执行。
当一个变量定义为 volatile 之后,将具备两种特性:
1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。
$锁的分类:
公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。
独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock
乐观锁/悲观锁
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap
而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized
。在Java 5通过引入锁升级的机制来实现高效Synchronized
。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。
独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock
乐观锁/悲观锁
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap
而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized
。在Java 5通过引入锁升级的机制来实现高效Synchronized
。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
典型的自旋锁实现的例子,可以参考自旋锁的实现
反射
1.通过反射获取属性
person p = new person();
Class<person> aClass = (Class<person>) p.getClass();
Field[] fields = aClass.getDeclaredFields();
for(Field f:fields){
System.out.println(f.getName() + "---" + Modifier.toString(f.getModifiers()) + "---" + f.getType());
}
2.通过反射获取方法
person p = new person();
Class<person> aClass = person.class;
Method gender = aClass.getDeclaredMethod("setScore", float.class);
gender.setAccessible(true);
gender.invoke(p,10);
System.out.println(p);
System.out.println(gender.getName() + "***" + Modifier.toString(gender.getModifiers()) + "***" + gender.getParameterCount());
3.通过反射获取构造器
Class<person> aClass = person.class;
Field[] declaredFields = aClass.getDeclaredFields();
Constructor<person> constructor = aClass.getDeclaredConstructor(new Class[]{String.class,Integer.class,boolean.class,float.class});
//需要指明获取构造器的参数类型和个数
person person1 = constructor.newInstance("Tom", 12,true,89);
System.out.println(person1);
注意:
默认获取的是public的属性、方法(和父类中公有的方法)、构造器,若要调用其他权限的,需要使用getDeclaredMethod(Filed)
可以使用xxx.setAccessible(true)强制修改私有数据或者调用私有方法
注解
注解的功能:
跟踪代码的依赖性,实现代替配置文件的功能
如何自定义一个注解:
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo{
String author() default "Pankaj";
String date();
int revision() default 1;
String comments();
}
- 注解方法不能有参数。
- 注解方法的返回类型局限于原始类型,字符串,枚举,注解,或以上类型构成的数组。
- 注解方法可以包含默认值。
- 注解可以包含与其绑定的元注解,元注解为注解提供信息,有四种元注解类型:
-
@Documented – 表示使用该注解的元素应被javadoc或类似工具文档化,它应用于类型声明,类型声明的注解会影响客户端对注解元素的使用。如果一个类型声明添加了Documented注解,那么它的注解会成为被注解元素的公共API的一部分。
-
@Target – 表示支持注解的程序元素的种类,一些可能的值有TYPE, METHOD, CONSTRUCTOR, FIELD等等。如果Target元注解不存在,那么该注解就可以使用在任何程序元素之上。
-
@Inherited – 表示一个注解类型会被自动继承,如果用户在类声明的时候查询注解类型,同时类声明中也没有这个类型的注解,那么注解类型会自动查询该类的父类,这个过程将会不停地重复,直到该类型的注解被找到为止,或是到达类结构的顶层(Object)。
-
@Retention – 表示注解类型保留时间的长短,它接收RetentionPolicy参数,可能的值有SOURCE, CLASS, 以及RUNTIME。
单例模式
饿汉式、懒汉式
懒汉式:
实现线程安全:静态内部类、加锁
实现可见性,避免重排序:将元素声明为volatile
避免通过反射创建对象:在构造器中声明异常
保证输入输出流传递的是同一个对象:重写resolve()方法
public class LazySingle {
private static volatile LazySingle lazySingle = null;//volatile设置线程的可见性,避免重排序
private LazySingle(){
throw new RuntimeException("error");//防止通过反射获取对象
}
public static LazySingle getInstance() {//实现序列化,提高安全性
if (lazySingle == null) {
//this.对象只能在非静态结构中使用
synchronized (LazySingle.class) {
if (lazySingle == null) {
lazySingle = new LazySingle();
}
}
}
return lazySingle;
}
public Object readResolve(){//使得被IO流获取的时候,传递的是同一个对象
return lazySingle;
}
}