Java(集合、泛型、反射、注解)
1.集合,数据的存储
2.泛型,参数化类型
3.反射,反向追溯
4.注解,框架中简化操作
一.数据结构
1.数组
Char[] cs = new Char[]{'G','U','P','A','O'}; 赋值,相当于初始化了大小。
Char[] cs1 = new Char[5];
cs1[0] = 'G';
....
特点:
1.内存地址连续,使用之前必须要指定数组长度
2.可以通过下标访问的方式访问成员,随机查询效率高
3.增删操作会给系统带来性能消耗[保证数据下标越界的问题,需要动态扩容]
2.链表
单向链表和双向链表
双向链表
特点
1.灵活的空间要求,存储空间不要求连续
2.不支持下标的访问,支持顺序遍历检索(顺序访问的速度也很快)
3.针对增删效率会更高些,只和操作节点的前后节点有关系,无需移动元素。
LinkedList
private static class Node<E> {
E item; // 节点的元素
Node<E> next; // 下一个节点
Node<E> prev; // 上一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
3.树
红黑树(黑平衡的二叉树)
红黑树,Red-Black Tree [RBT] 是一个自平衡【不是绝对】的二叉查找树,树上的节点满足如下的规则
1.每个节点要么是红色,要么是黑色。
2.根节点必须是黑色
3.每个叶子节点【NIL】是黑色,自动补全叶子节点。
4.每个红色节点的两个子节点必须是黑色
5.任意节点到每个叶子节点的路径包含相同数量的黑节点
黑平衡二叉树
1.recolor 重新标志节点为红色或者黑色
2.rotation 旋转 树达到平衡的关键
红黑树能自平衡,它靠的是什么?三种操作:左旋、右旋和变色
左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,
右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,
左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
变色:结点的颜色由红变黑或由黑变红。
红黑树插入的场景
1.红黑树为空
2.父节点为黑色节点
二.集合
Collection接口
数组形式【,】
Map接口
KV对
Iterator 迭代
工具类:
Collections
Arrays
比较器
Comparable Comparator
1. List接口
集合通用概念总览
- 集合的数据结构
- 遍历问题(一般用迭代器Iterator)
- 线程安全不安全
- 有序无序
(1).ArrayList
说到ArrayList就要考虑他的两个对立面(线程安全、有序无序)
ArrayList:有序、线程不安全
线程安全的数组类:CopyOnWriteArrayList,数据插入锁,读取不锁。ReentrantLock锁
有序的数组:LinkedList
本质就是动态数组,动态扩容(数组特征内存地址连续,增删慢,随机查找快)
/**
* Default initial capacity.
默认的数组的长度
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
空数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
集合中存储数据的 数组对象
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
* 集合中元素的个数
* @serial
*/
private int size;
初始操作
无参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
// this.elementData = {}
}
有参构造
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 初始长度大于0 就创建一个指定大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// {}数组赋值给 this.elementData
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
add方法
初始无参构造器
第一次添加
public boolean add(E e) {
modCount++;// 0->1
add(e, elementData, size);
return true;
}
// 新增元素、原有元素、原有元素长度,e {} 0
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
// 0 + 1
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
// 创建一个默认大小的数组,此处大小是DEFAULT_CAPACITY:10
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
第二次添加
elementData = {1,,,,,,,,,};
size = 1;
public boolean add(E e) {
modCount++;// 0->1
add(e, elementData, size);
return true;
}
// 新增元素、原有元素、原有元素长度,e {} 0
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
第十一次添加
elementData = {1,2,3,4,5,6,7,8,9,10};
size = 10;
public boolean add(E e) {
modCount++;// 11
add(e, elementData, size);
return true;
}
// 新增元素、原有元素、原有元素长度,e {1,2,3,4,5,6,7,8,9,10} 10
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
// minCapacity=11
private Object[] grow(int minCapacity) {
// oldCapacity=10
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 扩容10 ,11-10,20:子方法中扩容3倍
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
// 创建一个默认大小的数组,此处大小是DEFAULT_CAPACITY:10
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
get方法
public E get(int index) {
// 检查下标是否合法
Objects.checkIndex(index, size);
// 通过下标获取数组对应的元素
return elementData(index);
}
set方法
public E set(int index, E element) {
rangeCheck(index); // 检查下标
// 获取下标原来的值
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
remove方法
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
// es被删除的数组,i要删除数组的下标
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
// 删除后数组的大小,要大于下标的值
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
// 删除元素,删除后的数据重新拼接,最后一个元素设置成null
es[size = newSize] = null;
}
FailFast机制
快速失败的机制,Java集合类为了应对并发访问在集合迭代过程中,内部结构发生变化的一种防护措施,这种错误检查的机制为这种可能发生错误通过抛出 java.util.ConcurrentModificationException(并发修改异常)
为了保证对集合操作的一个原子性。
// for循环的迭代方式
for (Iterator iteratorTmp = list.iterator(); iteratorTmp.hasNext();){
iteratorTmp.next();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Iterator.next(): 该方法会比较迭代器获取到modCount游标和数组当前的modCount,不一致抛出ConcurrentModificationException
在for循环中进行元素的 remove/add操作,remove元素需要使用Iterator方式,如果是并发操作需要对Iterator对象加锁。
.next() 必须在 .remove() 之前调用
说明:
-
Iterator.next(): 该方法会比较迭代器获取到modCount游标和数组当前的modCount,不一致抛出ConcurrentModificationException
-
不能在for循环中进行元素的remove、add操作
-
remove元素需要使用Iterator方式,如果是并发操作需要对Iterator对象加锁。
-
.next() 必须在 .remove() 之前调用
(2).LinkedList(双向链表)
LinkedList查找数据时(使用for或者forEach时),都是把数据分成一半,从头开始或者从尾开始遍历,时间复杂度O(n^2)
使用Iterator,时间复杂度 O(n),从整个链表next去查。
LinkedList是通过双向链表去实现的,他的数据结构具有双向链表的优缺点,既然是双向链表,那么的它的顺序访问效率会非常高,而随机访问的效率会比较低,它包含一个非常重要的私有内部静态类:Node
private static class Node<E> {
E item; // 节点的元素
Node<E> next; // 下一个节点
Node<E> prev; // 上一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
get方法:本质上还是遍历链表中的数据
Node<E> node(int index) {
// assert isElementIndex(index);
// index 和 长度的一半比较
if (index < (size >> 1)) {
Node<E> x = first;
// 从头开始循环
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
// 从尾部开始循环
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
set方法
public E set(int index, E element) {
checkElementIndex(index);// 检查下标是否合法
Node<E> x = node(index); // 根据下标获取对应的node对象
E oldVal = x.item; // 记录原来的值
x.item = element; // 赋予新的值
return oldVal; // 返回修改之前的值
}
(3).Vector
和ArrayList很类似,都是以动态数组的形式来存储数据
Vector线程安全的
每个操作方法都加的有synchronized关键字,针对性能来说会比较大的影响,慢慢就被放弃了
(4).Collections: 同步数组
可以增加代码的灵活度,在我们需要同步是时候就通过如下代码实现
List syncList = Collections.synchronizedList(list);
本质上,重写增删改的操作。加锁
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
2. Set接口
(1).HashSet,本质是一个HashMap
概述
HashSet实现Set接口,由哈希表支持,它不保证set的迭代顺序,特别是它不保证该顺序永久不变,允许使用null。
由Hash算法计算的位置都是无序的
public HashSet() {
map = new HashMap<>();
}
add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
本质上是将数据保持在 HashMap中 key就是我们添加的内容,value就是我们定义的一个Object对象都一样。
特点
底层数据结构是哈希表,HashSet的本质是一个"没有重复元素"的集合,他是通过HashMap
实现的.HashSet中含有一个HashMap类型的成员变量map
.
自定义的对象存放在HashSet中,如果要保证数据唯一性,要重写hashCode、equals方法
(2).TreeSet
概述
基于TreeMap的 NavigableSet实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator进行排序,具体取决于使用的构造方法。
public TreeSet() {
this(new TreeMap<E,Object>());
}
本质是将数据保存在TreeMap中, key就是我们添加的内容,value是定义的一个Object对象。
3. Map接口
Map集合的特点
1.能够存储唯一的列的数据(唯一,不可重复) Set
2.能够存储可以重复的数据(可重复) List
3.值的顺序取决于键的顺序
4.键和值都是可以存储null元素的
(1). TreeMap
本质上就是红黑树
的实现
1.每个节点要么是红色,要么是黑色。
2.根节点必须是黑色
3.每个叶子节点【NIL】是黑色
4.每个红色节点的两个子节点必须是黑色
5.任意节点到每个叶子节点的路径包含相同数量的黑节点
Entry类:
K key; // key
V value; // 值
Entry<K,V> left; // 左子节点
Entry<K,V> right; // 右子节点
Entry<K,V> parent; // 父节点
boolean color = BLACK; // 节点的颜色 默认是黑色
(2). HashMap
线程安全问题:HashMap(线程不安全)、HashTable(线程安全,不建议使用)
有序无序:HashMap(无序)、TreeMap(有序)、LinkedHashMap(有序)
HashMap底层结构
Jdk1.7及以前是采用数组+链表
Jdk1.8之后 采用数组+链表 或者 数组+红黑树方式进行元素的存储
存储在hashMap集合中的元素都将是一个Map.Entry的内部接口的实现
存储顺序与插入顺序不一致。
LinkedHashMap: 存储顺序和插入顺序一致。
// 默认的HashMap中数组的长度 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// HashMap中的数组的最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认的扩容的平衡因子,默认大小16,当数组中元素达到12个时,对数组进行扩容。
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 链表转红黑树的 临界值
static final int TREEIFY_THRESHOLD = 8;
// 红黑树转链表的 临界值
static final int UNTREEIFY_THRESHOLD = 6
// 链表转红黑树的数组长度的临界值,数组长度大于64时才做红黑树的转换
static final int MIN_TREEIFY_CAPACITY = 64;
// HashMap中的数组结构
transient Node<K,V>[] table;
// HashMap中的元素个数
transient int size;
// 对HashMap操作的次数
transient int modCount;
// 扩容的临界值
int threshold;
// 实际的扩容值
final float loadFactor;
三.泛型
本质:参数化类型
泛型的擦除:
泛型只在编译阶段有效,编译之后JVM会采取去泛型化
的措施.
泛型在运行阶段是没有效果
1. 泛型通配符的介绍
(1).无边界通配符
<?> 通用的类型
public class Demo02 {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("gupao");
list1.add("bobo");
list1.add("mic");
loop(list1);
}
public static void loop(List<?> list){
for (int i = 0; i < list.size() ; i++) {
System.out.println(list.get(i));
}
}
}
(2).上边界通配符
< ? extends Number > 代表从Number往下的子类货孙类对象都可以使用
public class Demo03 {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("gupao");
list1.add("bobo");
list1.add("mic");
// loop(list1);
List<Number> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
}
/**
* ? extends Number
* 通用的类型必须是Number及其子类
* @param list
*/
public static void loop(List<? extends Number> list){
for (int i = 0; i < list.size() ; i++) {
System.out.println(list.get(i));
}
}
}
(3).下边界通配符
<? super Integer> 代表从Integer 到Object所有的对象都可以
public class Demo04 {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("gupao");
list1.add("bobo");
list1.add("mic");
loop(list1);
List<Number> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
loop(list2);
}
/**
* ? super Number
* 通用类型必须是Integer 到Object类型的对象
* @param list
*/
public static void loop(List<? super Number> list){
for (int i = 0; i < list.size() ; i++) {
System.out.println(list.get(i));
}
}
}
2. 泛型的具体的使用
规则:必须先声明再使用
泛型的声明是通过"<>"实现
约定泛型可以使用单个大写字母来表示 K E T V 等
(1). 泛型类
public class PersonNew <T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public PersonNew(T t) {
this.t = t;
}
}
增加我们代码的灵活度
(2). 泛型方法
public class Demo07 <K,V> {
/**
* 普通方法 可以使用 类中定义的泛型
* @param k
* @param v
* @return
*/
public K method1(K k,V v){
return (K)null;
}
/**
* 普通方法 使用方法中定义的泛型
* @param t
* @param v
* @param <T>
* @return
*/
public <T> T method2(T t,V v){
return (T)null;
}
/**
* 在静态方法中我们没法使用 类中定义的泛型
* @return
*/
public static <K> K method3(){
return null;
}
(3). 泛型接口
设计模式:模板方法中,可以使用泛型接口。
public interface CalGeneric <T> {
T add(T a,T b);
T sub(T a,T b);
T mul(T a,T b);
T div(T a,T b);
}
public class CalIntegerGeneric implements CalGeneric<Integer> {
@Override
public Integer add(Integer a, Integer b) {
return null;
}
@Override
public Integer sub(Integer a, Integer b) {
return null;
}
@Override
public Integer mul(Integer a, Integer b) {
return null;
}
@Override
public Integer div(Integer a, Integer b) {
return null;
}
}
四.反射
1.反射的定义
反向探知,在程序运行过程中动态的获取类的相关属性
这种动态获取类的内容以及动态调用对象的方法和获取属性的机制.就叫做JAVA的反射机制
反射的优缺点
优点
增加程序的灵活性,避免固有逻辑写死到程序中
代码相对简洁,可以提高程序的复用性
缺点
相比于直接调用反射有比较大的性能销毁
内部暴露和安全隐患
反射到底慢在哪里?
1.调用了native方法
2.每次newInstance都会做安全检查 比较耗时
2.反射的操作
2.1基本操作
1.获取类对象的四种方式
// 获取类字节码对象的四种方式
Class<User> clazz1 = User.class;
Class<?> clazz2 = Class.forName("com.gupao.edu.fs.User");
Class<? extends User> clazz3 = new User().getClass();
Class<?> clazz4 = Demo03.class.getClassLoader().loadClass("com.gupao.edu.fs.User");
2.基本信息操作
// 获取类的相关结构
System.out.println(clazz1.getModifiers()); // 获取类的修饰符
System.out.println(clazz1.getPackage());
System.out.println(clazz1.getName());
System.out.println(clazz1.getSuperclass());
System.out.println(clazz1.getClassLoader());
System.out.println(clazz1.getSimpleName());
System.out.println(clazz1.getInterfaces().length); // 获取类实现的所有的接口
System.out.println(clazz1.getAnnotations().length);
2.2属性字段的操作
/**
* Field操作
* @param args
*/
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
// 获取User对象
User user = userClass.newInstance();
// 获取类型中定义的字段 共有的字段以及父类中共有的字段
Field[] fields1 = userClass.getFields();
for(Field f:fields1){
System.out.println(f.getModifiers() + " " + f.getName());
}
System.out.println("--------------------");
// 可以获取私有的字段 只能够获取当前类中
Field[] fields2 = userClass.getDeclaredFields();
for(Field f:fields2){
System.out.println(f.getModifiers() + " " + f.getName());
}
// ***************************************************************************************************************
// 获取name字段对应的Field
Field nameField = userClass.getDeclaredField("name");
// 如果要修改私有属性信息那么我们需要放开权限
nameField.setAccessible(true);
nameField.set(user,"咕泡");
System.out.println(user.getName());
// 如果对静态属性赋值,静态属性属于类,并不属于对象,所以传入一个null即可。
Field addressField = userClass.getDeclaredField("address");
addressField.set(null,"湖南长沙");
System.out.println(User.address);
// ***************************************************************************************************************
}
2.3 类中的方法操作
public static void main(String[] args) throws Exception {
User user = new User();
Class<User> userClass = User.class;
// 可以获取当前类及其父类中的所有的共有的方法
Method[] methods = userClass.getMethods();
for (Method m : methods) {
System.out.println(m.getModifiers() + " " + m.getName());
}
System.out.println("**********");
// 获取本类中的所有的方法 包括私有的
Method[] declaredMethods = userClass.getDeclaredMethods();
for (Method m:declaredMethods){
System.out.println(m.getModifiers() + " " + m.getName());
}
Method jumpMethod = userClass.getDeclaredMethod("jump");
// 放开私有方法的调用
jumpMethod.setAccessible(true);
jumpMethod.invoke(user);
Method sayMethod = userClass.getDeclaredMethod("say", String.class);
// 静态方法调用
sayMethod.invoke(null,"咕泡666");
}
2.4 构造器的操作
/**
* 构造器的操作
* @param args
*/
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
// 获取所有的公有的构造器
Constructor<?>[] constructors = userClass.getConstructors();
for (Constructor c:constructors){
System.out.println(c.getModifiers() + " " + c.getName() );
}
System.out.println("************************");
// 获取所有的构造器
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
for (Constructor c:declaredConstructors){
System.out.println(c.getModifiers() + " " + c.getName() );
}
// 1.直接通过newInstance创建对象
User user = userClass.newInstance();
// 2.获取对应的Construcator对象获取实例
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, String.class);
// 私有的构造器调用需要放开权限
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor.newInstance("gupao","男"));
}
3.单例的漏洞
产生的原因是:反射可以调用私有的构造器造成的
public class PersonSingle {
private static PersonSingle instance;
private PersonSingle(){
if(instance != null){
throw new RuntimeException("实例已经存在了,不允许再创建...");
}
}
public static PersonSingle getInstance(){
if(instance == null){
instance = new PersonSingle();
}
return instance;
}
}
解决方案:在私有构造其中加入逻辑判断结合RuntimeException
处理即可
public static void main(String[] args) throws Exception {
PersonSingle p1 = PersonSingle.getInstance();
PersonSingle p2 = PersonSingle.getInstance();
PersonSingle p3 = PersonSingle.getInstance();
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
// 通过反射获取实例
Constructor<? extends PersonSingle> declaredConstructor = p1.getClass().getDeclaredConstructor();
declaredConstructor.setAccessible(true);
System.out.println( declaredConstructor.newInstance());
}
4. 反射的使用场景:
1.jdbc封装
2.SpringIOC
3.JdbcTemplate
4.Mybatis
…
5.反射的应用 SpringIOC
IOC 控制反转 就是一种设计思想,容器 管理对象
五.注解
1.注解的概念
注解: 说明程序的,给计算机看的
注释: 用文字描述程序,给程序员看的
/**
*
*/
//
定义:
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类:
1.编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
2.代码分析:通过代码里标识的注解对代码进行分析【使用反射】
3.编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
2.JDK预定义的注解
@Override:检查被该注解标注的方式是否是继承自父类【接口】
@Deprecated: 该注解表示注释的内容过时
@SuppressWarnings: 压制警告
all to suppress all warnings (抑制所有警告)
boxing to suppress warnings relative to boxing/unboxing operations(抑制装箱、拆箱操作时候的警告)
cast to suppress warnings relative to cast operations (抑制映射相关的警告)
dep-ann to suppress warnings relative to deprecated annotation(抑制启用注释的警告)
deprecation to suppress warnings relative to deprecation(抑制过期方法警告)
fallthrough to suppress warnings relative to missing breaks in switch statements(抑制确在switch中缺失breaks的警告)
finally to suppress warnings relative to finally block that don’t return (抑制finally模块没有返回的警告)
hiding to suppress warnings relative to locals that hide variable()
incomplete-switch to suppress warnings relative to missing entries in a switch statement (enum case)(忽略没有完整的switch语句)
nls to suppress warnings relative to non-nls string literals(忽略非nls格式的字符)
null to suppress warnings relative to null analysis(忽略对null的操作)
rawtypes to suppress warnings relative to un-specific types when using generics on class params(使用generics时忽略没有指定相应的类型)
restriction to suppress warnings relative to usage of discouraged or forbidden references
serial to suppress warnings relative to missing serialVersionUID field for a serializable class(忽略在serializable类中没有声明serialVersionUID变量)
static-access to suppress warnings relative to incorrect static access(抑制不正确的静态访问方式警告)
synthetic-access to suppress warnings relative to unoptimized access from inner classes(抑制子类没有按最优方法访问内部类的警告)
unchecked to suppress warnings relative to unchecked operations(抑制没有进行类型检查操作的警告)
unqualified-field-access to suppress warnings relative to field access unqualified (抑制没有权限访问的域的警告)
unused to suppress warnings relative to unused code (抑制没被使用过的代码的警告)
@SuppressWarnings("all")
public class AnnoDemo01 {
@Override
public String toString() {
return "AnnoDemo01{}";
}
@Deprecated
public void show1(){
// 发现过时了,功能更不上需求了
}
public void show2(){
// 功能更加强大的方法
}
public void demo(){
show1(); // 不推荐使用,但是可以使用
show2();
Date date = new Date();
date.getYear();
}
}
3.自定义注解
格式由两部分组成,元注解和属性列表
// 元注解
public @interface 注解名称{
// 属性列表
}
自定义的注解反编译后的内容
public interface MyAnno extends java.lang.annotation.Annotation {
}
注解的本质其实就是一个接口,继承Annotation父接口
/**
* 注解的本质就是接口
*/
public @interface MyAnno {
public String show();
}
属性:在接口中定义的抽象方法,叫做属性。
返回结果必须是如下类型
1.基本数据类型
2.String类型
3.枚举类型
4.注解
5.以上类型的数组
属性赋值注意点:
1.如果定义的属性时,使用default关键字给属性默认初始值,可以在使用注解是不赋值例如:int age() default 18;
2.如果只有一个属性需要赋值,而且该属性的名称是
value
,那么在赋值时value
可以省略3.数组赋值的时候,值使用
{}
包裹,如果数组中只有一个值,那么{}
可以省略
/**
* 注解的本质就是接口
*/
public @interface MyAnno {
String value();
MyAnno2 show4();
PersonEnum show5();
String[] show3();
//String name();
//int age() default 18; // 指定默认值 在使用注解的时候没有给该属性赋值,那么就使用默认值
/*String show1();*/
/*int show2();
String[] show3();
MyAnno2 show4();
PersonEnum show5();*/
}
@MyAnno(value="bobo",show4 = @MyAnno2,show5 = PersonEnum.P1,show3 = "a")
public void show2(){
// 功能更加强大的方法
}
4.元注解
JDK中给我们提供的4个元注解
1.@Target:描述当前注解能够作用的位置
ElementType.TYPE:可以作用在类上
ElementType.METHOD:可以作用在方法上
ElementType.FIELD:可以作用在成员变量上
2.@Retention: 描述注解被保留到的阶段
SOURCE < CLASS < RUNTIME
SOURCE:表示当前注解只在代码阶段有效
CLASS:表示该注解会被保留到字节码阶段
RUNTIME:表示该注解会被保留到运行阶段 JVM
自定义的注解:RetentionPolicy.RUNTIME
3.@Documented:描述注解是否被抽取到JavaDoc api中
4.@inherited:描述注解是否可以被子类继承
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnno3 {
}
5.自定义注解的案例
/**
* 自定义注解
* 该注解标明要执行哪个类中的哪个方法
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokAnno {
String className();
String methodName();
}
public class Student1 {
public void show(){
System.out.println("student1 show ....");
}
}
- 获取类对象
- 获取类对象中的注解
- 获取注解中的信息
@InvokAnno(className = "com.gupao.edu.anno2.Student1",methodName = "show")
public class MyMain {
public static void main(String[] args) throws Exception {
// 获取类对象
Class<MyMain> clazz = MyMain.class;
// 获取类对象中的注解
InvokAnno an = clazz.getAnnotation(InvokAnno.class);
/**
* 注解本质是 接口 获取到的其实是接口的实现
* public class MyInvokAnno implements InvokAnno{
*
* String className(){
* return "com.gupao.edu.anno2.Student1";
* }
* String methodName(){
* return "show";
* }
* }
*/
// 获取注解中对应的属性
String className = an.className();
String methodName = an.methodName();
System.out.println(className);
System.out.println(methodName);
// 通过反射的方式实现接口的功能
Class<?> aClass = Class.forName(className);
Method show = aClass.getDeclaredMethod("show");
// 方法的执行
Object o = aClass.newInstance();
show.invoke(o); // 执行对应的方法
}
}
执行哪个类中的哪个方法
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokAnno {
String className();
String methodName();
}
```java
public class Student1 {
public void show(){
System.out.println("student1 show ....");
}
}
- 获取类对象
- 获取类对象中的注解
- 获取注解中的信息
@InvokAnno(className = "com.gupao.edu.anno2.Student1",methodName = "show")
public class MyMain {
public static void main(String[] args) throws Exception {
// 获取类对象
Class<MyMain> clazz = MyMain.class;
// 获取类对象中的注解
InvokAnno an = clazz.getAnnotation(InvokAnno.class);
/**
* 注解本质是 接口 获取到的其实是接口的实现
* public class MyInvokAnno implements InvokAnno{
*
* String className(){
* return "com.gupao.edu.anno2.Student1";
* }
* String methodName(){
* return "show";
* }
* }
*/
// 获取注解中对应的属性
String className = an.className();
String methodName = an.methodName();
System.out.println(className);
System.out.println(methodName);
// 通过反射的方式实现接口的功能
Class<?> aClass = Class.forName(className);
Method show = aClass.getDeclaredMethod("show");
// 方法的执行
Object o = aClass.newInstance();
show.invoke(o); // 执行对应的方法
}
}