java知识点
2020-09-03 10:47:56 0 举报
AI智能生成
Java 最强知识点整理
作者其他创作
大纲/内容
java知识点
java中数据类型
原生数据类型
整数类型
1个字节:byte
2个字节:short
4 个字节:int
8个字节:long
字符类型
2个字节:char
浮点类型
4个字节:float
8个字节:double
精度丢失问题
布尔类型:boolean
值类型
用 a == b 判断相等
引用数据类型
包装类
Byte
Short
Integer
Long
Float
Double
Character
Boolean
包装类和原生数据类型选择
所有的POJO类属性必须 使用包装数据类型
RPC方法 的返回值和参数必须使用包装数据类型
所有的局部变量推荐使用基本数据 类型
流程控制
顺序结构
if else
while
switch(expression)
byte
short
char
int
枚举
String
do while
for
控制循环结构
break
continue
return
面向对象OOP
两个重要概念
类(class)
对象(object)
三大基本特征
继承(Inheritence)
关于继承的3点
父类有的,子类也有
父类没有的,子类可以增加
父类有的,子类可以改变
继承的注意事项
构造方法不能被继承
方法和属性可以被继承
子类的构造方法隐士地调用父类不带参数的构造方法
当父类没有不带参数的构造方法时,子类需要使用super来显式的调用父类的构造方法
extends
java单继承的
封装(Encapsulation)
多态(Polymorphism)
关键字
void
super
由子类访问父类中的实例属性和方法
直接查找父类
在子类覆盖父类方法时,访问父类同名方法
this
访问本类实例属性和方法
先找本类,没有则找父类
单独使用时,表示当前对象
this和super共同点
都是关键字,起指代作用
在构造方法中必须出现在第一行
重载
构造方法重载
方法重载
在编译器的 眼里,方法名称 + 参数类型 + 参数个数 ,组成一个唯一健,称为方法签名,JVM通过这个唯一健决定调用哪种重载的犯法
方法重写
抽象类
接口
抽象类和接口使用原则
优先选用接口
在既要定义子类的行为 ,又要为子类提供公共的功能时,应选择抽象类
static关键字
修饰属性
修饰方法
静态方法只能继承,不能重写
修饰类
代码块
final 关键字
属性
方法
类
成员变量赋值
在声明时就赋上初值
声明时不赋值,但在类的构造方法中为其赋上初值
static final 定义常量
new
初始化对象三个步骤
1、给Person该分配内存
2、调用Person类的构造方法
访问修饰符
public
被public修饰的属性和方法可以被所有类访问
protected
类内部
相同包
该类的子类
默认的
private
使用规则
如果不允许外部直接通过new创建对象,构造方法必须是private
工具类不允许有public 或 default构造方法
类非static成员变量并且 与子类共享,必须是protected
类非static 成员变量并且仅在本类使用,必须private
类static成员变量如果仅在本类 使用,必须是private
若是static成员变量,必须考虑是否为final
类成员方法只供类内部使用,必须为private
类成员方方法只对继承类公开,那么限制为protected
成员变量和局部变量区别
成员变量
实例变量(不以static修饰)
类变量(以static修饰
局部变量
形参(方法签名中定义的变量)
方法局部变量(在方法 内定义)
代码块局部变量(在代码块中定义)
变量使用的规则
考虑使用成员变量的情形
如果需要定义的变量时用于描述某个类或者某个对象的固有信息的
如果在某个类中需要以一个变量来保存该类或者实例运行时的状态信息
如果某个信息需要在某个类的多个方法 之间共享
成员变量危害
增大变量的生存时间,这将导致更大的内存开销
扩大了变量的作用域,这不利于提高程序的内聚性
类关系
继承
extends(is-a)
父类和子类
小狗继承动物
实现
implements(can-do)
小狗实现了狗叫的接口行为
组合
类 是成员变量(contains-a)
体现的是非常强的整体与部分的 关系,同生共死
头只能是身体强组合的一部分,两者完全不可分,具有相同的生命周期
聚合
类是成员变量(has -a)
可以拆分的整体与部分的关系,是非常松散的暂时 组合,部分可以被拆出来给另一个整体
小狗和狗绳之间是暂时聚合关系,狗绳完全可以复用在另一条小狗上
依赖
import类(use-a)
设计父类通常应该遵循如下规则
尽量隐藏父类的内部数据,尽量把父类的所有成员变量都设置成private访问类型,不要让子类直接访问父类的成员变量
不要让子类可以随意访问、修改类的方法
尽量不要再 父类构造器中调用将要被子类重写的方法
对象的引用
强引用(StrongReference)
软引用(Softreference)
弱引用(WeakReference)
虚引用(PhantomReference)
类内 方法
实例方法
非静态方法
依附于实例对象,可以通过引用变量调用其方法
当.class字节码文件加载之后,实例方法并不会被分配方法入口地址,只有在对象创建之后才会被分配
静态方法
类方法
当类加载后,即分配了相应的内存空间
静态方法中不能使用实例成员变量和实例方法
静态方法不能使用super和this关键字,这两个关键字指代的都是需要被创建出来的对象
通常静态方法用于定义工具类的按方法
静态代码块
在类加载 的时候就被调用,并且只执行一次
静态代码块先与构造方法 执行
静态代码块不存在于任何方法体内
getter 和 setter
满足面向对象语言封装的特性
有利于统一控制
类内定义方法 的顺序
公有方法或保护方法
私有方法
getter和setter方法
对象创建的几种方法
对象创建的几种方式
new
调用构造方法
clone
在内存上
反射
序列化
在文件上
通过Unsafe实例化一个类
集合(Collect)
Map
jdk8 Map接口新特性
putIfAbsent
putIfAbsent方法,只有在key不存在或者key为null的时候,value值才会被覆盖
源码如下:
map.putIfAbsent(\"t1\
if(map.containsKey(\"t1\")&&map.get(\"t1\")!=null) { map.put(\"t1\
使用场景:如果我们要变更某个key的值,我们又不知道key是否存在的情况下,而又不希望增加key的情况使用。
computeIfAbsent
在返回值上不一样,value值不存在的时候,返回的是新的value值,同时可以通过自定义一些条件进行过滤。
computeIfPresent
只有当key存在的时候才执行操作,那么我们把上面的需求变更下,对5岁以上的人进行分组,并且对8岁的人进行年龄加1操作。
HashMap
源码分析
具有很快的访问速度,但遍历顺序却是不确定的。(无序性)
HashMap最多只允许一条记录的键为null,允许多条记录的值为null。
HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。
HashMap的线程不安全主要体现在resize时的死循环及使用迭代器时的fast-fail上
如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap
1.8 HashMap
HashMap是一种散列表,采用(数组 + 链表 + 红黑树)的存储结构
HashMap的默认初始容量为16(1<<4),默认装载因子为0.75f,容量总是2的n次方;
HashMap扩容时每次容量变为原来的两倍;
当桶的数量小于64时不会进行树化,只会扩容;
当桶的数量大于64且单个桶中元素的数量大于8时,进行树化;
当单个桶中元素数量小于6时,进行反树化;
HashMap是非线程安全的容器;
HashMap查找添加元素的时间复杂度都为O(1);
LinkedListHashMap
LinkedListHashMap 分析
有序
可以理解为一个维护了元素次序的HashMap
TreeMap
有序的集合
红黑树结构
TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
TreeMap 实现了Cloneable接口,意味着它能被克隆。
TreeMap 实现了java.io.Serializable接口,意味着它支持序列化。
HashTable
Hashtable是遗留类,与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的。
并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁
Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换
ConcurrentHashMap
锁分段技术或CAS(JDK8及以上)
java8
jdk8
锁的粒度是HashEntry(首节点)
JDK1.8使用红黑树来优化链表,基于长度很长的链表的遍历是一个很漫长的过程,而红黑树的遍历效率是很快的,代替一定阈值的链表,这样形成一个最佳拍档
JDK1.8版本的数据结构变得更加简单,使得操作也更加清晰流畅,因为已经使用synchronized来进行同步,所以不需要分段锁的概念,也就不需要Segment这种数据结构了,由于粒度的降低,实现的复杂度也增加了
java7
jdk7
锁的粒度是基于Segment的,包含多个HashEntry
WeakHashMap
ConcurrentSkipListMap
线程安全的有序的哈希表,适用于高并发的场景
通过跳表实现的
HashMap、TreeMap、LinkedListHashMap区别
HashMap可实现快速存储和检索,但其缺点是其包含的元素是无序的,这导致它在存在大量迭代的情况下表现不佳
LinkedHashMap保留了HashMap的优势,且其包含的元素是有序的。它在有大量迭代的情况下表现更好。
TreeMap能便捷的实现对其内部元素的各种排序,但其一般性能比前两种map差。
ConcurrentHashMap 、ConcurrentSkipListMap 区别
ConcurrentSkipListMap 的key是有序的
ConcurrentSkipListMap 支持更高的并发
非多线程的情况下,应当尽量使用TreeMap
对于并发性相对较低的并行程序可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供较好的效率
高并发程序,应当使用ConcurrentSkipListMap
ConcurrentSkipListMap、TreeMap区别
它们的线程安全机制不同,TreeMap是非线程安全的,而ConcurrentSkipListMap是线程安全的
ConcurrentSkipListMap是通过跳表实现的,而TreeMap是通过红黑树实现的
Collection
List
ArrayList
内部动态数组的实现
添加的时候有序,按照index遍历也是有序的
ArrayList 线程安全问题
线程安全问题
LinkedList
是一个继承于AbstractSequentialList的双向链表。它可以被当成堆栈、队列或双端队列进行操作。
实现了List接口,能对它进行队列操作。
实现了Deque接口,能当作双端队列使用
实现了java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
可以保持插入时的顺序
ArrayList 和 LinkedList区别
是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。
是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。
CopyOnWriteArrayList
写 时复制
写加锁,复制一个容器
读不加锁
Vector
数组 实现
读加锁,写 加锁
Set
Hashset
特点
不能保证元素的排列顺序,顺序 可能与添加顺序不同,顺序也有可能发生变化
HashSet内部使用HashMap的key存储元素,以此来保证元素不重复;
HashSet是无序的,因为HashMap的key是无序的
HashSet中允许有一个null元素,因为HashMap允许key为null;
对于HashSet中保存的对象,请注意正确重写其equals和hashCode方法,以保证放入的对象的唯一性。
LinkedHashSet
对LinkedHashSet来讲,和HashSet与HashMap的关系类似,LinkedHashSet底层是通过LinkedHashMap来实现的,只要搞懂了LinkedHashMap,自然就懂了LinkedHashSet。
TreeSet
TreeSet底层也是通过相应的TreeMap来实现的
使用方式
自然顺序(Comparable)
TreeSet类的add()方法中会把存入的对象提升为Comparable类型
调用对象的compareTo()方法和集合中的对象比较
根据compareTo()方法返回的结果进行存储
比较器顺序(Comparator)
创建TreeSet的时候可以制定 一个Comparator
add()方法内部会自动调用Comparator接口中compare()方法排序
ConcurrentSkipListSet
ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的
ConcurrentSkipListSet是线程安全的
CopyOnWriteArraySet
Queue
阻塞队列
ArrayBlockingQueue
基于数组的阻塞队列实现
内部没有实现读写分离
有界队列
使用一把锁实现
LinkedBlockingQueue
基于链表的阻塞队列
锁分离(两把锁)
可以有界也可以无界
PriorityBlockingQueue
基于优先级的阻塞队列,传入对象必须传comparable接口
内部线程采用的是公平锁
无界队列
DelayQueue
元素必须实现Delayed接口
没有大小限制的队列
应用场景
对缓存超时的数据进行移除
当向缓存中添加key-value对时,如果这个key在缓存中存在并且还没有过期,需要用这个key对应的新过期时间
为了能够让DelayQueue将其已保存的key删除,需要重写实现Delayed接口添加到DelayQueue的DelayedItem的hashCode函数和equals函数
当缓存关闭,监控程序也应关闭,因而监控线程应当用守护线程
任务超时处理(例如订单超时,饿了么订餐:下单后60s之后给用户发送短信通知))
实现方式
使用数据库定时任务,每隔几秒扫描订单表,找出超时订单后关闭
使用spring的@Scheduled注解启动定时任务或者使用Quartz任务管理器,定时触发任务,处理超时订单
使用消息中间件,Active或者RocketMQ提供了延迟消息队列,下单后往延迟消息队列中发消息,超时后,消费端会接收到一条延迟的 订单消息,并做相应处理
使用DelayQueue来实现
如何使用DelayQueue延迟队列处理超时订单?
其他方式
空闲连接的关闭
SynchronousQueue
一种没有缓冲的队列,生产者产生的数据直接会被消费者获取并消费
非阻塞对列
PriorityQueue
ConcurrentLinkedQueue
线程安全的队列
使用循环CAS的方式
基于链接节点的无界线程安全队列
它采用先进先出的规则对节点进行排序
对比
ConcurrentLinkedQueue与LinkedBlockingQueue对比?
两者都是线程安全的队列;
两者都可以实现取元素时队列为空直接返回null,后者的poll()方法可以实现此功能
前者全程无锁,后者全部都是使用重入锁控制的
前者效率较高,后者效率较低
前者无法实现如果队列为空等待元素到来的操作
前者是非阻塞队列,后者是阻塞队列
前者无法用在线程池中,后者可以
Collections
排序操作(主要针对List接口相关)
reverse(List list):反转指定List集合中元素的顺序
shuffle(List list):对List中的元素进行随机排序(洗牌)
sort(List list):对List里的元素根据自然升序排序
查找和替换(主要针对Collection接口相关)
max(Collection coll):返回最大元素
min(Collection coll):返回最小元素
frequency(Collection Object o):返回指定集合中指定对象出现的次数
同步控制
synchronizedSet
synchronizedSortedSet
synchronizedList
synchronizedMap
synchronizedSortedMap
设置不可变集合
emptyXxx():返回一个空的不可变的集合对象
singletonXxx():返回一个只包含指定对象的,不可变的集合对象。
unmodifiableXxx():返回指定集合对象的不可变视图
. 其它
Comparator<T> reverseOrder(Comparator<T> cmp)
元素的比较
Comparable 和 Comparator
Comparable
自己和自己比较,可以看作是自营性质的比比较
比较方法 compareTo
典型应用:TreeSet
Comparable相当于“内部比较器”,
Comparator
第三方比较器,可以看作 是平台性质的比较器
比较方法:compare
典型应用:Arrays.sort
我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序
Comparator相当于“外部比较器”
hashCode 和 equals
如果两个对象的equals的结果是相等的,则两个对象的hashCode的返回结果也必须相同的
fail-fast 和 fail-safe机制
fail-fast机制
什么是同步修改
集合元素值可以为null
什么是fail-fast机制
Fail-fast 机制实现原理
单线程环境下的fail-fast
ArrayList中的subList
多线程环境下fail-fast
fail-safe 机制
避免fail-fast
迭代器的remove方法
使用java并发包(java.uti.concurent)中的类来代替ArrayList 和 HashMap;
泛型
泛型的命名规范
N Number 数字
泛型类
泛型 接口
泛型方法
泛型方法,是在调用方法的时候指明泛型的具体类型
example1
example2
泛型通配符
非限定通配符
限定符
<? extends T>
<? super T>
好处
类型安全。放置的是什么,取出来的自然是什么,不用担心抛出ClassCastException异常。
提升可读性。从编码极端就显式的知道泛型集合、泛型方法等处理的对象类型 是什么》
代码重用。泛型合并了同类型的处理代码,使代码重用度变高
java基础类库
System
RunTime
Object
Objects
Math
Random
ThreadLocalRandon
BigDecimal
时间类
Date
Calendar
SimpleDateFormat
正则表达式
String 类
matches(String regex)
split(String regex)
Pattern
Matchers
find()
group()
start()
end()
lookingAt()
matches()
reset
字符串
只读字符串,对它的任何改动,其实都是创建一个新对象,再把引用指向该对象
String对象复制操作后,会在常量 池中进行缓存,如果下次申请创建对象时,缓存中已经存在,则直接返回相应引用给创建者
StringBuffer
在 原对象上进行修改,是线程安全的
StringBuilder
非线程安全带额
java clone
浅克隆
直接内存栈区的复制
深度克隆
拷贝内存栈区 和 堆区
克隆 和 new 的区别
java 中 clone 和 new 都可以创建 对象
clone不会调用构造方法;new 会调用构造方法
clone能快速创建一个已有对象的副本,即创建对象并且将已有对象中的属性值克隆;new只能在JVM中申请一个空的内存区域,对象的属性 值要通过构造方法赋值
内部类
静态内部类
static class StaticInnerClass{}
作用域不会扩散到包外
可以通过\"外部类.内部类\"的方式直接访问
内部类可以访问外部类中的所有静态属性和方法
成员内部类
private class InstanceInnerClass{}
局部内部类
定义在方法或者表达式内部
匿名内部类
(new Thread(){}).start();
java原生序列化
java序列化
Hessian序列化
JSON 序列化
枚举类
枚举用法
枚举类的构造器只能使用private 访问控制符,如果省略了构造器的访问控制符,则 默认使用private修饰;
枚举的所有 实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。
枚举类默认提供了values()方法,该方法可以很方便得遍历所有的枚举值
功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的方法
java.lang.reflect
Class类
获得类相关的方法
getClassLoader()\t
获得类的加载器
forName(String className)\t
根据类名返回类的对象
getName()\t
获得类的完整路径名字
newInstance()\t
创建类的实例
getPackage()\t
获得类的包
getSimpleName()\t
获得类的名字
getInterfaces()\t
获得当前类实现的类或是接口
getSuperclass()\t
获得当前类继承的父类的名字
获得类中属性相关的方法
getField(String name)
获得某个公有的属性对象
getFields()\t
获得所有公有的属性对象
getDeclaredField(String name)\t
获得某个属性对象
getDeclaredFields()\t
获得所有属性对象
获得类中注解相关的方法
getAnnotation(Class<A> annotationClass)\t
返回该类中与参数类型匹配的公有注解对象
getAnnotations()\t
返回该类所有的公有注解对象
getDeclaredAnnotation(Class<A> annotationClass)\t
返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()\t
返回该类所有的注解对象
获得类中构造器相关的方法
getConstructor(Class...<?> parameterTypes)\t
获得该类中与参数类型匹配的公有构造方法
getConstructors()\t
获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes)\t
获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()\t
获得该类所有构造方法
获得类中方法相关的方法
获得该类某个公有的方法
getMethods()\t
获得该类所有公有的方法
获得该类某个方法
getDeclaredMethods()\t
获得该类所有方法
类中其他重要的方法
isAnnotation()\t
如果是注解类型则返回true
isAnnotationPresent(Class<? extends Annotation> annotationClass)\t
如果是指定类型注解类型则返回true
isAnonymousClass()\t
如果是匿名类则返回true
isArray()\t
如果是一个数组类则返回true
isEnum()\t
如果是枚举类则返回true
isInstance(Object obj)\t
如果obj是该类的实例则返回true
isInterface()\t
如果是接口类则返回true
isLocalClass()\t
如果是局部类则返回true
isMemberClass()\t
如果是内部类则返回true
Field类
equals(Object obj)\t
属性与obj相等则返回true
get(Object obj)\t
获得obj中对应的属性值
设置obj中对应属性值
Method类
传递object对象及参数调用该对象对应的方法
Constructor类
newInstance(Object... initargs)\t
根据传递的参数创建类的对象
获取对应类的Class对象
Class.forName(\"java.lang.String\")
使用类的.class语法: String.class
使用对象的getClass()方法,String s = \"aa\";Class<?> clazz = s.getClass();
通过类的不带参数的构造方法生成对象
先获取Class对象,然后通过该Class对象的newInstance()方法直接生成即可
先获取Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstace()方法生成
通过带参数的构造方法
注解
什么是注解
注解分为3类
java自带的标准注解
@Override
标明重写某个方法
@Deprecated
标明某个类或方法过时
@SuppressWarning
标明要忽略的警告
@Documented
标明是否生成javadoc文档
元注解,元注解用于 定义注解的注解
@Retention
标明注解被保留的阶段
@Target
标明注解使用的范围
@Inherited
标明注解可继承
自定义注解
可以根据自己的需求定义注解
注解的用途
生成文档,通过代码里标识的元数据生成javadoc文档。
编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例
注解使用演示
定义一个可以注解在METHOD上的注解
定义一个可以注解在FIELD上的注解
定义一个可以注解在PARAMETER上的注解
注解的实现原理
注解三要素
注解声明
ElementType.TYPE:用于描述类、接口或enum声明
ElementType.FIELD:用于描述实例变量
ElementType.METHOD
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另一个注释
ElementType.PACKAGE 用于记录java文件的package信息
RetentionPolicy
SOURCE级别表示代码级别可见,经过编译器编译生成字节码对象时,此注解就没了。
CLASS表示字节码对象级别可见,但是字节码对象被虚拟机加载时,
RUNTIME表示运行时也可见,当虚拟机加载字节码对象时,此注解仍然可见。
使用注解的元素
使用注解没什么好说的就是在你需要的地方加上对应的你写好的注解就行
操作注解使其起作用
<T extends Annotation> T getAnnotation(Class<T> annotationClass)返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
Annotation[] getAnnotations():返回该程序元素上存在的所有注解
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响
注解内部的定义
日志
记录日志的原因
记录 操作轨迹
监控系统运行状况
回溯系统故障
日志规范
命名规范
appName_logType_lognName
logType:日志类型,推荐有stats、monitor、visit等
logName为 日志描述
日志级别
DEBUG
INFO
WARN
ERROR
FATAL
不同日志级别的处理方式
预先判断日志级别
避免无效打印日志
区别对待日志
保证记录内容完整
记录异常时一定要输出异常堆栈,例如logger.error(\"XXX\
日志中如果输出对象实例,要确保实例类 重写了toString方法,否则只会输出对象的hashCode值,没有实际以及
记录日志考虑三个问题
日志是否有人看
看这条日志能干设呢么
能不能提升问题排查效率
日志框架
log4j
logback
jdk-logging
SLF4j(Simple Logging Facade for java)
apache commons loggin(JCL)
日志框架三大部分
1、日志门面
3、日志库
2、日志适配器
日志使用场景
java日志体系
日志框架的设计
异常
系统异常设计的出发点
良好的异常信息展示,开发运维人员能快速定位问题
响应外部调用异常 时,应能明确指明是内部异常还是调用条件不满足导致
响应用户操作异常时,能友好的提示用户‘’
如何做到以上3点
需要对异常进行分类
内部异常
资源 环境异常(系统环境异常、数据库连接超时、第三方服务响应超时)
第三方服务错误响应
第三方响应结果错误
外部传入参数非法
错误的编码逻辑
异常的业务数据
业务异常
用户操作错误
业务条件不满足
正确捕获这类异常
方法入参合法性检验
对系统外部提供的 接口,是必须进行参数校验
系统内部对外层提供接口,进行验证
工具类进行参数验证
public方法进行验证
private方法(不建议参数验证)
第三方响应结果合法性验证
获取第三方 方法结果后,根据你们约定进行验证
业务处理前,对业务前置进行条件验证
业务处理后对结果进行验证
对于可能出现异常的代码进行try catch 捕获
尝试恢复处理
直接抛出
转换后抛出;
在系统出口统一拦截处理
Web Response
内部异常:引导至异常提示页
业务异常:返回对应提示消息 至前端
未知异常:尝试进行认别,如果认识不了,转换成编码异常
Http Api
内部异常:返回接口不可用消息
参数错误:基于API文档的异常列表进行响应返回。表名参数非法,需要调用方加强参数合法性验证
业务错误:基于接口约定返回对应code 和 消息
RPC Service响应
内部异常:返回消息不可用消息
参数错误:基于接口文档进行响应,直接返回异常堆栈信息
业务错误:直接返回异常堆栈
处理思路
哪里发生异常
谁来处理异常
如何处理异常
异常分类
Throwable
Error(致命异常)
StackOverflowError
OutOfMemeoryError
此类错误,程序无法处理,只能人工接入
Exception(非致命异常)
checked异常
SQLException
ClassNotFoundException
IOException
细分
无能为力、引起注意型
力所能及、坦然处置 型
unchecked异常
可预测异常
IndexOutOdBoundsException
NullPointerException
此类异常不应该被产生或者 抛出,而应该提前做好边界检查,空指针判断等处理
可透出异常
主要是指框架或者系统产生的且会自行处理的异常,而 程序无需关系
try代码块
try
catch
可选执行的代码块 ,如果没有任何异常发生则不会执行;如果发现异常则进行处理或者向上抛出。这一切都在catch代码 快照红执行
finally
必选执行的代码块,不管是否有异常产生,即使发生OutofMemoryError也会执行,通常用于善后清理工作。
如果finally代码块没有执行,有三种可能
1.没有进入try代码块
2.进入 try代码块,但是代码运行中出现了死循环或者死锁状态
3.进入try代码块,但是执行了Sytem.exit()操作
注意:finally 是在return 表达式运行后执行的,此时将要return的结果已经被暂存起来,待finally代码块执行结束后再将之前暂存的结果返回
finally的职责:不是对变量进行赋值等操作,而是清理资源、释放连接、关闭管道流等操作
不要再finally 加上return语句
Lock、ThreadLocal、InputStream等这些需要进行强制释放和清除的对象都得在finally代码块中进行显示的清理,避免产生内存泄漏或者资源消耗
异常的抛与接
传递异常信息的方式
对外:提供的开放接口使用错误码
公司内部:跨应用远程服务调用优先考虑使用Result对象来封装错误码 、错误信息描述
应用内部:推荐直接抛出异常对象
IO流
什么是IO?
IO 是指为数据传输所提供的输入输出流,其输入输出对象可以是: 文件、网络服务、内存等
字节流
InputStream
FileInputStream
FilterInputStream
BufferedInputStream
DataInputStream
readInt
readBoolean
readShort
readLong
...
ObjectInputStream
ByteArrayInputStream
OutputStream
FileOutputStream
FilterOutputStream
BufferedOutputStream
DataoutputStream
WriteBoolean
writeShort
writeLong
PrintStream
ObjectOutputStream
ByteArrayOutputStream
字符流
Reader
BufferedReader
InputStreamReader
FileReader
StringReader
ByteArrayReader
Writer
BufferedWriter
OutputStreanWriter
FileWriter
CharArrayWriter
RandomAccessFile
流操作的基本规律
明确数据源和数据汇(数据目的)
明确操作的数据是否是纯文本数据
数据源
键盘(System.in)
硬盘(File)
内存(数组)
数据汇
控制台System.out
硬盘File
Java中的资源文件的读取
java虚拟机
java并发编程
网络编程
Btrace
sun公司推出的一款Java 动态、安全追踪(监控)工具,可以在不用重启的情况下监控系统运行情况,方便的获取程序运行时的数据信息,如方法参数、返回值、全局变量和堆栈信息等,并且做到最少的侵入,占用最少的系统资源
实现原理
Attach API + BTrace脚本解析引擎 + ASM + JDK Instumentation。
使用
引用包
btace.zip
安装步骤
window
下载安装压缩包,最新版本的是1.2.1,下载地址: https://github.com/btraceio/btrace
解压缩,命令脚本是放在bin目录中
设置脚本环境变量,加入PATH 设置加入path路径,输入命令echo $PATH 能找到可执行文件所在路径
linux
HelloWorld
语法
btrace $pid script.java;;所以需要知道要探测的 Java程序的进程id,然后编写一个探测脚本即可
命令格式
btrace [-I <include-path>] [-p <port>] [-cp <classpath>] <pid> <btrace-script> [<args>]
示例:btrace -cp common.jar 1200 AllCalls1.java
include-path指定头文件的路径,用于脚本预处理功能,可选;
port指定BTrace agent的服务端监听端口号,用来监听clients,默认为2020,可选;
classpath用来指定类加载路径,默认为当前路径,可选;
pid表示进程号,可通过jps命令获取;
btrace-script即为BTrace脚本;btrace脚本如果以.java结尾,会先编译再提交执行。可使用btracec命令对脚本进行预编译。
args是BTrace脚本参数,在脚本中可通过\"$\"和\"$length\"获取参数信息,可选;
exmaple:
HelloWorld
TraceHelloWorld
使用场景
BTrace 是一个事后工具,所谓事后工具就是在服务已经上线了,但是发现存在以下问题的时候,可以用 BTrace。
比如哪些方法执行太慢,例如监控执行时间超过1s的方法
查看哪些方法调用了 System.gc() ,调用栈是怎样的
查看方法参数或对象属性
哪些方法发生了异常
当在Map中插入大量数据,分析其扩容情况;
为了更好解决问题,最好还要配合事前准备和进行中监控,事前准备就是埋点嘛,在一些可能出现问题的方法中进行日志输出,进行中监控就是利用一些实时监控工具,例如 VisualVM 、jmc 这些带界面的工具或者 jdk 提供的命令行工具等,再高级一点的就是利用 Graphite 这样的Metrics 工具配合 web 界面展示出来
使用限制
不能调用实例方法以及静态方法(com.sun.btrace.BTraceUtils除外)
不能将目标程序和对象赋值给BTrace的实例和静态field
不能有同步块和方法
不能有循环
拦截方法定义
@OnMethod 可以指定 clazz 、method、location。由此组成了在什么时机(location 决定)监控某个类/某些类(clazz 决定)下的某个方法/某些方法(method 决定)。
使用全限定名:clazz=\"com.metty.rpc.common.BtraceCase\
使用正则表达式:clazz=\"/javax\\\\.swing\\\\..*/\
使用接口:clazz=\"+com.ctrip.demo.Filter\
使用注解:clazz=\"@javax.jws.WebService\
如果需要分析构造方法,需要指定method=\"<init>\"
如何定位
直接定位到一个类下的一个方法,上面测试用的例子就是
正则表达式定位
按接口或继承类定位
拦截时机
拦截时机由 location 决定,当然也可为同一个定位加入多个拦截时机,即可以在进入方法时拦截、方法返回时拦截、抛出异常时拦截
Kind.Entry与Kind.Return
表示异常被 throw 、异常被捕获还有异常发生但是没有被捕获的情况,在拦截函数的参数定义里注入一个Throwable的参数,代表异常
Kind.Call 和 Kind.Line
Kind.Call 表示被监控的方法调用了哪些其他方法,例如:
其他例子
查看谁调用了GC
打印耗时超过100ms的方法
https://github.com/btraceio/btrace/tree/master/samples
注意问题
如果出现 Unable to open socket file: target process not responding or HotSpot VM not loaded 这个问题,可能的原因是执行 BTrace 脚本的用户和 Java 进程运行的用户不是同一个,使用 ps -aux | grep $pid查看一下 Java 进程的执行用户,保证和 BTrace 脚本执行用户相同即可
Btrace能做的事情太多,但使用之前切记检查脚本的可行性,一旦Btrace脚本侵入到系统中,只有通过重启才能恢复。
相关插件
greys-anatomy
Guava
javaagent&javassist
0 条评论
回复 删除
下一页