Java基础知识
零一、命令提示符
启动:开始+R cmd;
切换盘符 :盘符名称;
进入文件夹:cd文件夹名称
回到上一级:cd: cd\根路径
查看内容:dir
清屏:cls
退出:exit
零二、数组
1.动态初始化数组的格式: 数据类型[] 数组名称= new 数据类型[数组长度] ;
2.静态初始化数组的格式: 数据类型[] 数组名称= new 数据类型[]{元素1,元素2....} ;
3.省略静态初始化数组的格式: 数据类型[] 数组名称={元素1,元素2....} ;
4.动态初始化数组元素自动拥有一个默认值:
整数类型:0 字符类型:'\u0000' 引用类型:null
浮点类型:0.0 布尔:false
5.获取数组长度格式: int len=数组名称.length ;
6.数组的遍历输出:
for(int i=0;i<数组.length;i++){sout(数组(i));}
零三、面向对象
三大特性: 封装,继承,多态
位置 | 局部变量 | 成员变量 |
---|---|---|
定义位置不同 | 在方法内部 | 在类中 |
默认值不同 | 没有默认值 | 没有默认值会有赋值 |
1、封装
方法就是一个封装
private是一个封装
当方法中的局部变量和类的成员变量重名时,根据"就近原则"优先使用局部变量,使用成员变量改为this.变量.
构造方法: public 类名称(参数类型,参数名称){方法体;}(快捷键:Alt+INs)
一个标准的类下包含四部分
1. 所有成员变量都用private关键字修饰
2. 为每一个成员变量编写一对Getter/Seter方法(快捷键:Alt+INs)
3. 编写无参构造方法(快捷键:Alt+INs)
4. 编写有参构造
2、继承
主要解决的问题是共性抽取
父类格式:public class 父类名称{};
子类格式:public class 子类名称 extends 父类名称{};
访问有两种方式:
1.直接通过对象访问成员变量,等号左边是谁就优先用谁,没有向上找;
2.间接通过成员方法方法,该方法属于谁就用谁,没有则向上找;
局部变量:直接写
本类的成员变量:this.成员变量
父类的成员变量:super.成员变量
重写
概念:在继承关系中,方法的名称一样参数列表也一样;
注意事项:
1.参数相同,名字相同 前面写@Override
2.子类方法的返回值必须小于等于父类的返回值范围
3.子类方法的权限必须大于等于父类方法的权限修饰符
public>protected>(default)>private
4.私有的不能被继承不能被重写
父类的构造方法的访问特点
1.子类构造方法中默认包含一个super();
2.可以通过super关键字调用父类重载构造
3.super的父类构造调用必须是子类构造方法第一句,不能一个子类构造调用多次super构造
抽象方法所在的类必须是抽象类->abstract class
public abstract void 方法名();
使用抽象类和抽象方法
1.不能直接new抽象对象
2.必须有一个子类继承父类
3.子类必须重写父类中所有的抽象方法
4.创建子类对象使用
3、多态
口诀:
方法:编译看左边,运行看右边;
成员变量:编译看左边,运行看左边;
向上转型
格式: 父类名称 对象名=new 子类名称();或接口名称 对象名=new实现类名称();
向下转型
格式: 子类名称 对象名=(子类名称)父类对象;
instanceof关键字
格式: boolean 对象名 instanceof 类名称 判断是否是这个的子类
4、重载
多个方法名称一样,但参数列表不一样
好处:只需记住一个方法名称,就可以实现类似的多个功能
方法重载与下列因素有关
1.参数个数不同
2.参数类型不同
3.参数多类型顺序不同
方法重载与下列因素无关
1.与参数名称无关
2.方法的返回值无关
零四、Scanner
创建对象:Scanner sc=new Scanner(System.in);
获取键盘输入int数字:int num=sc.nextInt();
获取键盘输入一个(不带空格)的字符串:String str=sc.next();
获取键盘输入一个(带空格)的字符串:String str=sc.nextLine();
零五、匿名对象
格式 : new 类名称();
注意:匿名对象只能使用唯一的一次
使用建议:如果有一个对象只使用一次,就可以使用匿名对象
作为参数:
方法名(new Scanner(System.in));
方法名(Scanner sc){
sout(" "+sc.nextInt);
}
作为返回值:
main方法中{
Scanner sc=方法名();
sout(sc.next());
}
public static Scanner 方法名(){
return new Scanner(System.in);
}
零六、Random(随机数)
创建对象: Random 对象名=new Random();
获取随机数: int num=对象名.nextInt(参数);
参数可以确定数字的取值范围:左闭右开区间如:输入10则范围为0~9
零七、字符串String
构造方法:
1. public String();
2. public string(char[] array);
3. public string(byte[] array);
常用方法:
1. public String concat(String str);将字符串与参数字符串拼接返回新字符串.
2. public char charAt(int index);获取索引位置的单个字符;
3. public int indexof(String str);查找参数字符串在当前字符串中第一次出现的 位置,没有返回-1.
4. public String substring (int beginIndex);返回一个字符串,从beginIndex开始截取字符串到字符
串结尾.
5. public String substring (int beginIndex,int endIndex);返回一个字符串,从beginIndex到endIndex截取字符串,
包含beginIndex不包含endIndex.
6. public boolean str.endswith(String str);判断参数是否在结尾
转换方法:
1. public char[] tocharArray();将当前字符串拆成字符数组作为返回值;
2. public byte[] getBytes();获取底部的字节数组;
3. public String replace(charsequence oldString,charsequence rewstring);
将所有老字符串替换成新的字符串并返回;
字符串对比:
1. public boolean equalsIgnorecase(String str);不区分大小写
2. public boolean equals(Object obj);区分大小写,只有都是字符串内容相同才会给true否则都是false.
分割字符串:
public String[] split(String regox);
注意:参数是一个正则表达式,如果按照英文句点划分,必须与"\\.";
零八、static
注意事项:静态都只能直接静态不能访问非静态,因为先有静态后有非静态
静态代码块:在类中写 Static{//静态代码块的内容}
特点:当第一次用到本类时,静态代码块执行唯一的一次;
静态优先非静态;
典型用途:用于一次性地对静态成员变量进行赋值.
零九、工具类
1、Arrays工具类
1. public static String toString(数组); 遍历数组
2. public static void sort(数组); 从小到大排序
2、Math数字工具类
1.public static double abs(double num);绝对值
2.public static double ceil(double num);向上取整
3.public static double floor(double num);向下取整
4.public static long round(double num); 四舍五入
5.Math.PI 代表近似的圆周率
3、Collections工具类
**常用功能**
public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素。
public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。
public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。
comparator和comparable的区别:
comparable:自己(this)和别人(参数)比较,自己需要事项Comparable重写比较规则campareTo方法
comparator:相当于找了一个第三方当裁判,比较两个
comparator的排序规则:o1-o2:升序
一十、接口
格式:public interface 接口名称{
//接口内容
}
使用步骤:
1.接口不能直接使用,必须有一个实现类来实现接口
格式:public class 实现类名称 implements 接口名{
//...
}
2.重写:去掉abstract关键字加上方法体大括号
3.创建实现类的对象并使用
方法:
1.默认方法:default
2.静态方法:static->不能通过接口实现类的对象调用接口中的静态方法
格式: 接口名称.静态方法名(参数);
3.私有方法:格式: private 返回值 方法名(参数){方法体};
4.静态私有方法: private static 返回值 方法名(参数){方法体};
5."成员变量"一旦使用final关键字,说明不可改变
格式: public static final 数据类型 常量名称=数据值;
一十一、final
常用的四种用法:
1.可以用来修饰一个类->不能有子类(太监类)能有父类;
2.可以用修饰一个方法->不能被覆盖重写
3.可以用来修饰一个局部变量 "一次赋值,终身不变"
4.可以用来修饰一个成员变量
abstract和final关键字不能同时使用,会产生矛盾;
final修饰一个成员变量 要么直接赋值,要么通过构造方法赋值,二者选其一;
权限修饰符
位置(从大到小) | public | protected | (default) | private |
---|---|---|---|---|
同一个类 | √ | √ | √ | √ |
同一个包 | √ | √ | √ | × |
不同包 | √ | √ | × | × |
不同包非子类 | √ | × | × | × |
一十二、内部类
1.成员内部类
格式:
修饰符 class 外部类名称{
修饰符 class 内部类名称{}
}
使用方式:
1.间接方式:在外部类的方法中,使用内部类,然后,main只是调用外部类的方法
2.直接方式:公式:外部类名称.内部类名称 对象名=new 外部类名称.new 内部类名称();
出现重名现象:
外部类名称.this.外部类成员变量名
2.局部内部类(包含匿名内部类)
外部类 public、(default)
内部类 public 、protected 、(default)、private
局部内部类什么都不可以
匿名内部类的定义格式:
接口名称 对象名 =new 接口名称(){
覆盖重写所有的抽象方法
}
注意:匿名内部类,在创建对象的时候只能使用唯一一次;
一十三、Object
1.oString方法
public String toString() :返回该对象的字符串表示。
toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。所以我们要在类中覆盖重写
toString() (快捷键Alt+Ins);
2.equals方法
public boolean equals(Object obj) :指示其他某个对象是否与此对象“相等”。
默认比较的是地址值,只要不是同一个对象就会一直返回false所以我们也需要覆盖重写 equals (快捷键Alt+Ins)
一十四、Time时间类
1、Date类
public Date() :配他的时间(精确到毫秒);
public Date(long date); - 分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日
00:00:00 GMT)以来的指定毫秒数。
常用方法: public long getTime() 把日期对象转换成对应的时间毫秒值。
tips: 由于我们处于东八区,所以我们的基准时间为1970年1月1日8时0分0秒。
2、DateFormat类
构造方法: public SimpleDateFormat(String pattern) :用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat
常用的格式规则为:
yyyy 年 MM月 dd 日 HH 时 mm分 ss秒 EEE星期 DDD一年里的多少天
常用方法
public String format(Date date)将Date对象格式化为字符串
public Date parse(String source)将字符串解析为Date对象
3、Calendar类
创建对象名: Calendar 对象名=Calendar.getInstance();
常用方法:
public int get(int filed);返回给定日历字段的值
public void set(int filed,int Value)- 将给定的日历字段设置为给定值。
public abstract void add(int field, int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量。
public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。
Calendar类中提供很多成员常量,代表给定的日历字段:
字段值 | 含义 |
---|---|
YEAR | 年 |
MONTH | 月(从0开始,可以+1使用) |
DAY_OF_MONTH | 月中的天(几号) |
HOUR | 时(12小时制) |
HOUR_OF_DAY | 时(24小时制) |
MINUTE | 分 |
SECOND | 秒 |
DAY_OF_WEEK | 周中的天(周几,周日为1,可以-1使用) |
一十五、System类
System.currentTimeMillis():返回以毫秒为单位的当前时间。
system.arraycopy((数组名)Object src ,(起始位置)int srcpos,(数组名)Object dest,(起始位置)int destpos,
(复制长度)int lenght)将数组中指定的数据拷贝到另一个数组中
一十六、StringBuilder类
构造方法:
public StringBuilder();构造一个空的StringBuilder容器
public StringBuilder(String str);构造一个StringBuilder容器并将字符串添加进入
常用方法:
public StringBuilder append();添加任意类型数据的字符串形式,并返回当前对象的自身
public String toString();将其转换为String对象
一十七、包装类
基本数值->包装对象
Integer i=new Integer(4);
Integer ii=Integer.valueof(4);
包装对象->基本数值
int num=i.intvalue();
基本数据类型到String类型直接在后面加" "
除了character以外其余转换为对应的基本数据类型应为 包装类名.parse基本类型(str)
tips:从jdk1.5以后开始出现了自动装箱与自动拆箱
一十八、集合
一、Collection集合(单列集合)
有两个子接口
|-List接口 特点:元素有序,元素可重复,元素有索引值;
|-ArrayList集合
|-LinkedList集合
|-Vector集合
|-Set接口 特点:元素无序,元素不可重复;
|-HashSet集合
|-LInkedHashSet集合 特点:是一个有序的集合
|-TreeSet集合
Collection集合定义了单列集合框架中的共性内容;是所有单列集合的父接口
常用功能:
public boolean add(E e);给定的对象添加到集合中
public void clear();清空所有元素
public boolean remove(E e);把给定的对象在集合中删除
public boolean contains(E e);判断当前的集合中是否包含给定的对象
public boolean isEmpty();判断当前集合是否为空
public int size();返回集合中元素的个数
public Object[] toArray();把集合中的元素存储到数组中
1、List接口
JDK9中添加了of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList等待
常用方法:
public void add(int index, E element);将指定元素添加到该集合中的指定位置
public E get(int index)返回指定位置的元素
public E remove(int index); 移除指定位置的元素,返回值是被移除的元素
public E set(int index,E element)用指定元素替换集合中指定位置的元素
1)ArrayList集合
//E代表泛型
创建对象: ArrayList<E> 对象名=new ArrayList<>();
常用方法:
1.添加:public boolean add(E e);
2.查看:public E get(int Index);
3.删除:public remove(int index);
4.获取长度:public int size();
2)LinkedList集合
常用方法(了解):
public void addFirst(E e);将指定元素插入此列表的开头;
public void addLast(E e);将指定元素添加到此列表的结尾;
public E getFirst();返回此列表的第一个元素
public E getLast();返回此列表的最后一个元素
public E removeFirst();移除并返回此列表的第一个元素
public E removeLast();移除并返回此列表的最后一个元素
public E pop();从此列表所表示的堆栈处弹出一个元素等效于removeFirst()
public boolean isEmpty();如果列表不包含元素则返回ture
public void push(E e);将元素推入此列表所表示的堆栈等效于addFirst()
2、Set接口
JDK9中添加了of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList等待
继承自Collection接口,它与Collection接口中的方法基本一致,不同的是更加严格,并且当中的元素无序,并且都会以某种规则
保证存入的元素不出现重复
1)HashSet集合(了解后期补充)
HashSet底层的实现其实是一个java.util.HashMap支持
HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:
hashCode与equals方法。
2)LinkedHashSet集合
HashSet元素添加进入后无顺序,要保证有序就会使用到LinkedHashSet集合
3)可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:
修饰符 返回值类型 方法名(参数类型... 形参名){ }
其实这个书写完全等价与 修饰符 返回值类型 方法名(参数类型[] 形参名){ }
只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。
JDK1.5以后。出现了简化操作。... 用在参数上,称之为可变参数
二、Map集合(双列集合)
JDK9中添加了of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList等待
特点:
1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个Value)
2.Map集合中的元素,两个值可以相同也可以不同
3.Map集合中的元素,key不允许重复,Value可以重复
4.Map集合中的元素,Key和Value是一一对应的
常用子类:
1、 HashMap<k,v> - 存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的
hashCode()方法、equals()方法。
2、 LinkedHashMap<K,V> :HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证
元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
常用方法:
public V put(K key,V value);把指定的键和值添加到Map集合中
public V remove(Object key);把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
public V get(Object key);根据键获取指定位置的值
public contiansKey(Object key)判断集合中是否包含指定的键
public Set<K> keyset();获取所有键存储到Set集合中
Public Set<Map.Entry<k,v>> entrySet();获取所有的键值对对象的集合(Set集合)
Map集合遍历键找值方式
键找值方式:即通过元素中的键,获取键所对应的值
1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示: keyset()
2. 遍历键的Set集合,得到每一个键。
3. 根据键,获取键所对应的值。方法提示: get(K key)
Entry键值对对象
Entry表示一对键和值,获取键和值得方法
public K getKey(); 获取键
public V value(); 获取值
方式:
1. 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示: entrySet() 。
2. 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。
3. 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示: getkey() getValue()
1、属性集【Properties类】
构造方法:
public Properties():创建一个空的属性列表。
基本的存储方法:
public Object setProperty(String key, String value): 保存一对属性。
public String getProperty(String key):使用此属性列表中指定的键搜索属性值。
public Set<String> stringPropertyNames():所有键的名称的集合。
与流相关的方法:
public void load(InputStream inStream): 从字节输入流中读取键值对9ik,
一十九、Iterator迭代器
public Iterator iterator() ;获取集合对应的迭(die)代器,用来遍历集合中的元素
常用方法:
public E next();返回迭代的下一个元素
public boolean hasNext();如果仍有元素可以迭代,则返回true;
增强for格式:
for(元素的数据类型 变量名:Collection集合or数组){
写操作代码
}
二十、泛型
使用的好处:
将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
避免了类型强转的麻烦。
定义含有泛型的类: 修饰符 class 类名<代表泛型的变量>{}
定义含有泛型的方法格式: 修饰符 <代表泛型的变量> 返回值类型 方法名(参数){}
定义含有泛型的接口: 修饰符 interface 接口名<代表泛型的变量>{}
泛型的通配符: ? 代表任意的数据类型
使用格式:不能创建对象使用,只能作为方法的参数使用
通配符的高级使用->受限泛型
泛型的上限限定 类型名称 <? extends 类 > 对象名称 只能接收该类型及其子类
泛型的下限限定 类型名称 <? super 类 > 对象名称 只能接收该类型及其父类
二十一、异常
异常体系
Throwable体系:
Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。
常用方法:
public void printStackTrace():打印异常的详细信息
public String getMessage()获取发生异常的原因
public String toString()获取异常的类型和异常的描述信息(不用)
异常处理的五个关键字: try 、 catch 、 finally 、 throw 、 throws ;
抛出异常:throw 关键字:
使用格式: throw new xxxException(异常处理的原因);
Objects非空判断
public static <T> T requireNonNull(T obj) :查看指定引用对象不是null
声明异常throws
格式: 修饰符 返回值类型 方法名(参数) throws 异常类名..{ }
捕获异常:
try..catch 可以使用多个catch
格式:
try{
可能产生异常的代码
}catch(){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
如何获取异常信息:
public String getMessage(); 获取异常的描述信息,,原因(提示给用户的时候,就提示错误原因
public String toString(); 获取异常的类型和异常描述信息(不用)
public void printStackTrace(); 打印异常的跟踪栈信息并输出到控制台
finally代码块:
语法: try...catch...finally
自定义异常
1. 自定义一个编译期异常: 自定义类 并继承于java.lang.Exception。
2. 自定义一个运行时期的异常类:自定义类 并继承于java.lang.RuntimeException。
二十二、线程
创建线程类的第一种方法:
实现步骤:
1.创建一个Tread的子类
2.重写ThRead类中的run方法
3.创建Thread子类的对象
4.调用Thread类中的start方法,开启新线程执行run
1、Tread类
构造方法:
public Thread();分配一个新的线程对象;
public Tread(String name);分配一个指定名字的新的线程的对象
public Tread(Runable trarget):分配一个带有指定目标新的线程对象
public Thread(Runnable target,String name)分配一个带有指定目标新的线程对象并指定名字
常用方法:
public String getName();获取当前线程名称
public void start();导致此线程开始执行
public void run();此线程要执行的任务在此处定义代码
public static void sleep(long millis);暂时停止执行
public static Thread currentThread();返回对当前正在执行的线程对象的引用
创建线程类的第二种方法:
实现步骤:
1.创建Runnable接口的实现类
2.重写Runnable接口的run方法设置任务
3.创建一个Runnable接口的实现类对象
4.创建Thread类对象构造方法传递Runnable接口的实现类对象
5.调用Thread类的start方法,开启新线程执行run
设置线程的名称
新线程的默认名称:Thread-n,n是0从开始依次递增
主线程默认名称:main
两种方式:
(1)setName(String name)
(2)生成两个构造方法,带字符串参数的构造必须调用父类带字符串参数的构造,传递线程的新名称
2、Runnable接口
好处:也可以说两种方式的区别
实现Runnable接口比继承Thread类所具有的优势:
1. 适合多个相同的程序代码的线程去共享同一个资源。
2. 可以避免java中的单继承的局限性。
3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
3、匿名内部类
其实是一个没有名字的局部内部类由于它没有名字,使用的时候必须创建对象
格式:
new 父类/接口(){
// 重写父类方法
// 重写接口方法
}
原理:在创建类的子类对象,或者实现接口
常规方式使用接口
(1)创建一个类,实现接口
(2)在类中重写接口中的所有抽象方法
(3)创建实现类对象
匿名内部类将常规的三个步骤一次搞定,简化操作,省略了一个.java文件
4、线程安全
1.同步代码块 synchronized
synchronized(同步锁){
同步代码
}
同步锁好处:
1.锁对象可以是任意类型
2.多个线程对象可以使用同一把锁
2.同步方法
使用步骤:synchronized修饰的方法叫做同步方法
格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
3.Lock锁
接口中的方法:
void Lock(); 获取锁
void unLock(); 释放锁
使用步骤:
1.在成员位置创建一个ReentrantLock对象
2.在可能出现安全问题的代码前调用方法Lock()获取锁
3.在可能出现安全问题的代码最后位置调用unlock()释放锁
5、线程状态
线程的6种状态:
1. 新建 NEW :new创建线程对象,Thread或者子类对象
2. 阻塞 BLOCKED :调用start方法,此时线程具有CPU的执行资格,但是没有执行权
3. 可运行 RUNNABLE :有CPU的执行权,正在运行
4. 死亡 TERMINATED (被终止):执行完run方法,或者出现异常,或者强制调用stop
5. 计时等待 TIMED_WAITING :调用了sleep(时间)或wait(时间)方法
6. 永久等待 WAITING (无限等待):调用了wait()方法,调用notify()或者notifyAll()可以唤醒
方法:
void wait();无限等待
void wait(long timeout);计时等待
void wait(long timeout,int nanos);计时等待
void notify();随时唤醒一个正在等待的线程
void notifyAll();唤醒所有正在等待的线程
面试题:
wait和sleep的区别
1)wait是Object类中的方法,非静态方法
sleep是Thread类的方法,静态方法
2)wait除了重载的有参方法,还有无参方法
sleep只有重载有参方法
3)wait只能用在同步中,调用只能使用锁对象调用
sleep可以用在同步中,也可以不用在同步中,通过类名调用
4)wait会释放锁
sleep在同步中使用不会释放锁,会抱着锁睡
6、线程池
Executors类中有个创建线程池的方法如下:
public static ExecutorService newFixedThreadPool(int nThreads ):返回线程池对象。
(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)
参数: int nThreads :创建线程池中包含线程的数量
返回值: ExecutorService接口 返回的是实现类对象
关闭/销毁线程池的方法: void shutdown()
步骤:
1.创建线程池对象
2.创建Runnable接口子类对象
3.提交Runnable接口子类对象
4.关闭线程池(一般不做)
线程池的原理
其实就是一个容器,用集合实现,里面初始的时候存放了一堆的线程需要是使用的时候就从容器中取出(remove(索引)),使用完之后不是
销毁该线程,而是放回(add(线程))到线程池中。
线程池的好处:
不需要重复的创建线程,销毁线程,
(1)提高了效率
(2)提高了响应的速度
(3)方便管理
二十三、Lambda表达式
Lambda表达式的标准格式
由三部分组成:
a.一些参数
b.一个箭头
c.一段代码
格式: (参数)->{一些重写的代码}
Lambda表达式的省略格式:
省略规则
在Lambda标准格式的基础上,使用省略写法的规则为:
1. 小括号内参数的类型可以省略;
2. 如果小括号内有且仅有一个参,则小括号可以省略;
3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
二十四、File类
概念:是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作.
构造方法:
public File(String pathname);通过给定的字符串转换的抽象路径名,创建新的FIle
public File(String parent,String child);
public File(File parent,String child);
常用方法:
public String getAbsolutePath();返回此File的绝对路径字符串
public String getPath();将此File转换为路径字符串
public String getName();返回由此File表示的文件或目录的名称
public long length();返回由此File表示的文件的长度(字节)
public long lastModified()最后的修改的时间
判断功能的方法:
public boolean exists();此File表示的文件或者目录是否存在
public boolean isDirectory();此File表示的是否为目录
public boolean isFile();此File表示的是否为文件
绝对路径和相对路径:
绝对路径:从盘符开始的路径,一个完整的路径
相对路径:相对于项目目录的路径,这是一个便携的路径,开发中经常使用
符号:
directory :文件夹/目录
path :路径
pathSeparator 路径分隔符.win: ; linux: :
separator 文件名称分割符 win: \ linux: /
创建删除功能的方法:
public boolean createNewFile();当且仅当具有该名称的文件尚不存在时,创建新的空文件
public boolean deleate();删除由File表示的文件或目录
public boolean mkdir();创建由此File表示的目录(单个文件夹)
public boolean mkdirs();创建由此File表示的目录,包括任何必须但不存在的父目录
目录的遍历:
public String[] list();返回一个String数组,表示该File目录中的所有子文件或目录;
public File[] listFiles();返回一个File数组,表示该File目录中的所有的子文件或目录;
文件过滤器
FileFilter是File的过滤器
File[] listFile()
File[] listFiles(FileFilter filter)
抽象方法:boolean accept(File pathname)测试pathname是否应该包含在当前File目录中,符合则返回true
File[] listFiles(FilenameFilter filter)过滤文件名
抽象方法:boolean accept(File dir,String name)
二十五、递归
种类:直接递归和间接递归
直接递归称为方法自身调用自己。
间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法
注意事项:
1.递归一定有条件限定
2.虽然有限定,但次数不能太多
二十六、IO流
分类:
根据数据的流向分为:输入流和输出流。
1.输入流 :把数据从其他设备上读取到内存中的流。
2.输出流 :把数据从内存 中写出到其他设备上的流。
格局数据的类型分为:字节流和字符流。
1.字节流 :以字节为单位,读写数据的流。
2.字符流 :以字符为单位,读写数据的流。
顶级父类们:
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
###一、字节流
1、 字节输出流【OutputStream】
字节输出流的基本共性功能方法:
public void close():关闭此输出流并释放与此流相关联的任何系统资源。
public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
public abstract void write(int b):将指定的字节输出流。
1)FileOutputStream
构造方法
public FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件。
写出字节数据:
1.写出字节: write(int b)
2.写出字节数组: write(byte[] b)
3.写出指定长度字节数组: write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节
数据追加续写:
public FileOutputStream(File file, boolean append) :创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name, boolean append) : 创建文件输出流以指定的名称写入文件。
写出换行
回车符: \r
换行符: \n
win系统:每行结尾是 回车+换行 \r\n
Unix系统里,每行结尾只有 换行 ,即 \n ;
Mac系统里,每行结尾是 回车 ,即 \r 。从 Mac OS X开始与Linux统一
2、字节输入流【InputStream】
字节输入流的基本共性功能方法:
public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
public abstract int read() : 从输入流读取数据的下一个字节。
public int read(byte[] b) :从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
1)FileInputStream类
构造方法:
FileInputStream(File file) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象
file命名。
FileInputStream(String name) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名
name命名
读取字节数据:
1.读取字节: read方法 ,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1.
2.使用字节数组读取: read(byte[] b) ,每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
二、字符流
1、字符输入流【Reader】
字符输入流的基本共性功能方法:
public void close() :关闭此流并释放与此流相关联的任何系统资源。
public int read() : 从输入流读取一个字符。
public int read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
1)FileReader类
构造方法:
FileReader(File file) : 创建一个新的 FileReader ,给定要读取的File对象。
FileReader(String fileName) : 创建一个新的 FileReader ,给定要读取的文件的名称
读取字符数据:
1. 读取字符: read 方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1
2.使用字符数组读取:read(char[] cbuf) ,每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1
2、字符输出流【Writer】
字节输出流的基本共性功能方法:
void write(int c) 写入单个字符。
void write(char[] cbuf) 写入字符数组。
abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数
void write(String str) 写入字符串。
void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
void flush() 刷新该流的缓冲。
void close() 关闭此流,但要先刷新它
1)FileWriter类
构造方法:
FileWriter(File file) : 创建一个新的 FileWriter,给定要读取的File对象。
FileWriter(String fileName) : 创建一个新的 FileWriter,给定要读取的文件的名称
基本写出数据
写出字符:write(int b) 方法,每次可以写出一个字符数据
1.写出字符数组 :write(char[] cbuf) 和 write(char[] cbuf, int off, int len),每次可以写出字符数组中的数据
用法类似 FileOutputStream
2.写出字符串:write(String str) 和 write(String str, int off, int len),每次可以写出字符串中的数据,更为方便
3.续写和换行:操作类似于 FileOutputStream
三、缓冲流
缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:
字节缓冲流: BufferedInputStream , BufferedOutputStream
字符缓冲流: BufferedReader, BufferedWriter
1.字节缓冲流
构造方法:
public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
public BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流
2.字符缓冲流
构造方法:
public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
public BufferedWriter(Writer out) : 创建一个新的缓冲输出流
特有方法:
BufferedReader : public String readLine() : 读一行文字。
BufferedWriter : public void newLine() : 写一行行分隔符,由系统属性定义符号
四、转换流
1. InputStreamReader类
构造方法:
InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。
InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。
2.OutputStreamWriter类
构造方法:
OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流
五、序列化
1、ObjectOutputStream类
构造方法:
public ObjectOutputStream(OutputStream out) : 创建一个指定 OutputStream 的 ObjectOutputStream 。
序列化操作:
1. 一个对象要想序列化,必须满足两个条件:
1)该类必须实现Serializable 接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出
NotSerializableException
2)该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient
关键字修饰。
2.写出对象方法:
public final void writeObject (Object obj) : 将指定的对象写出
2、ObjectInputStream类
构造方法:
public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。
反序列化操作:
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:
public final Object readObject () : 读取一个对象
六、打印流【PrintStream类】
构造方法:
public PrintStream(String fileName) : 使用指定的文件名创建一个新的打印流
二十七、网络编程
三要素:
1.协议:计算机网络通信必须遵守的规则
2.Ip地址:指互联网协议地址(Internet Protocol Address),俗称IP
常用命令:
本机IP地址: ipconfig
检查网络是否连通: ping 空格 IP地址
特殊的IP地址: 127.0.0.1 、 localhost
3.端口号:用两个字节表示的整数,它的取值范围是0~65535
一、TCP通信
客户端:创建 Socket 对象
服务端:创建 ServerSocket 对象
1、Socket类
构造方法:
public Socket(String host, int port) :
创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。
成员方法:
public InputStream getInputStream() : 返回此套接字的输入流。
如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
关闭生成的InputStream也将关闭相关的Socket。
public OutputStream getOutputStream() : 返回此套接字的输出流。
如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
关闭生成的OutputStream也将关闭相关的Socket。
public void close() :关闭此套接字。
一旦一个socket被关闭,它不可再使用。
关闭此socket也将关闭相关的InputStream和OutputStream 。
public void shutdownOutput() : 禁用此套接字的输出流。
任何先前写出的数据将被发送,随后终止输出流。
2、ServerSocket类 :
构造方法:
public ServerSocket(int port) :
使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。
成员方法:
public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接
二十八、函数式接口
函数式接口在Java中是指:有且仅有一个抽象方法的接口
格式:
修饰符 interface 接口名称 {
public abstract 返回值类型 方法名称(可选参数信息);
// 其他非抽象方法内容
}
@FunctionalInterface注解
定义在接口上判断你的代码是否有且仅有一个抽象方法
常用的函数式接口:
1、Supplier接口
Supplier 接口仅包含一个无参的方法: T get() ,用来获取一个泛型参数指定类型的对象数据.
2、Consumer接口
Consumer<T> 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据有数据类型由泛型决定.
抽象方法:accept
Consumer 接口中包含抽象方法 void accept(T t) 意为一个指定泛型的数据
默认方法:andThen
如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合
而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代码:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) ‐> { accept(t); after.accept(t); };
}
3、Predicate接口
有时我们需要对数据进行判断,从而得到一个boolean值得结果,就可以使用 Predicate<T> 接口
1.抽象方法:test
Predicate 接口中包含一个抽象方法: boolean test(T t) .用于条件判断的场景.
2.默认方法:and
既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,
可以使用default方法 and 。其JDK源码为:
default Predicate<T> and(Predicate<? super T> other){
Objects.requireNonNull(other);
return (t) ‐> test(t) && other.test(t);
}
3.默认方法:or
与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) || other.test(t);
}
4.默认方法:negate
“与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法 negate 的JDK源代码为
default Predicate<T> negate() {
return (t) ‐> !test(t);
}
4、Function接口
Function<T,R> 接口用来根据一个类型的数据的到另外一个类型的数据
抽象方法:apply
Function 接口中最主要的抽象方法为: R apply(T t) 根据类型T的参数获取类型R的结果
使用场景:将 String 类型转换为 Integer 类型
默认方法:andThen
Function 接口中有一个默认的 andThen 方法,用来进行组合操作。JDK源代码如:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after){
Objects.requireNonNull(after);
return (T t) ‐> after.apply(apply(t));
}
该方法同样用于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多:
二十九、Stream流
Steam 流是Java8中新加入的最常用的流接口(并不是一个函数式接口)
获取流的几种常用方式:
1.所有的 Collection 集合都可以通过 Stream 默认方法获取流
2. Stream 接口的静态方法 of 可以获取数组对应的流
根据Collection获取流
Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流
根据Map获取流
Map 接口不是Collection 的子接口,且K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分 Key 、Value 或entry等情况
根据数组获取流
由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of
常用方法:
1.逐一处理:forEach
2.过滤:filter
3.映射:map
4.统计个数:count
5.取用前几个:limit
6.跳过前几个:skip
7.组合:concat
1.逐一处理: forEach
void forEach(Consumer<? super T> action
该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。
*简而言之:用于遍历流中的数据
2.过滤: filter
可以通过 filter 方法将一个流转换为另一个子集流:格式为:
Stream<T> filter(Predicate<? super T> predicate);
接收一个 predicate 函数式接口参数(可以是一个Lambda或方法引用)作为 筛选条件
*简而言之:用于过滤流中的数据
3.映射: map
如果需要将流中的元素映射到另一个流中,可以使用 map 方法,格式为:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型数据
*简而言之:用于将流中数据转换为另一种数据
**4.统计个数:**count
正如集合 collection 当中的 size 方法,流提供 count 方法来数一数其中的元素个数:
long count();
该方法返回一个long值代表元素个数,(不再想旧集合那样的int值)
*简而言之:用于统计流中元素的个数
**5.取用前几个:**limit
limit方法可以对流进行截取,只取用前n个.格式:
Stream<T> limit(long maxSize)
参数是一个long类型,如果集合当前长度大于参数则进行截取;否则不进行操作
*简而言之:用于截取流中的前n个元素
**6.跳过前几个:**skip
如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:格式为:
Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流.
*简而言之:用于跳过流中的前n个元素
**7.组合:**concat
如果有两个流合并成为一个流,那么可以使用 Stream 接口的静态方法 concat 格式为:
static <T> Stream<T> concat(Stream<? extends T> a,Stream<? extends T>b);
简写 public static Stream concat(Stream a, Stream b)
*简而言之:用于合并两个流,将元素合在一起
收集Stream结果可以将Stream流对象转换为集合或者数组:**
1)将Stream流对象转换为数组(通过Stream接口中的toArray方法)
Object[] toArray();
例如: Object[] objects = stream2.toArray();
<A> A[] toArray(IntFunction<A[]> generator);
例如: String[] strings = stream2.toArray(value->new String[value]);
2)将Stream流对象转换为集合(通过Stream接口中的方法)
<R, A> R collect(Collector<? super T, A, R> collector);
Collector 对应一个工具类 Collectors 里面有一些方法可以直接返回Collector对象
Collectors中的方法:
public static <T> Collector<T, ?, List<T>> toList() :转换为List 集合。
例如:List<String> list = stream2.collect(Collectors.toList());
public static <T> Collector<T, ?, Set<T>> toSet() :转换为Set 集合。
例如:Set<String> set = stream2.collect(Collectors.toSet());
一.方法引用符
双冒号 :: 为引用运算符
作用:用于简化Lambda表达式的书写
成员方法的引用的分类:(4种)
对象引用成员方法
格式: 对象名::成员
方法名类名引用静态方法
格式: 类名名::静态成员方法名
super引用父类方法
格式: super::父类的成员方法名
this引用本类方法
格式: this::本来的成员方法名
构造方法引用:(2种)
类的构造方法引用
格式: 类名::new
数组的构造方法引用
格式: 数据类型[]::new
三十、Junit单元测试
测试分类:
1.黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值
2.白盒测试:需要写入代码.关注程序具体的执行流程
一、Junit使用:白盒测试
步骤:
1. 定义一个测试类(测试用例)
建议:
测试类名:被测试的类名Test 例如:CalculatorTest
包名:xxx.xxx.xx.test 例如:cn.itcast.test
2. 定义测试方法:可以独立运行
建议:
方法名:test测试的方法名 例如:testAdd()
返回值:void
参数列表:空参
3. 给方法加@Test
4. 导入junit依赖环境
判定结果:
红色:失败
绿色:成功
一般我们会使用断言操作来处理结果
Assert.assertEquals(期望的结果,运算的结果);
补充:
@Before:
修饰的方法会在测试方法之前被自动执行
@After:
修饰的方法会在测试方法执行之后自动被执行
二、反射
反射:框架设计的灵魂
学习反射就是学习一堆的类的使用,包括Class、Constructor、Field和Method类
什么是反射
在运行阶段可以获取类的成员(构造方法,成员变量和成员方法等),并使用这些成员会将这些获取出来的成员封装成对应的对象,
构造方法Constructor、成员变量Field、成员方法Method
反射也称为Java中的解剖学,对类进行解剖,获取类中的成员
反射的步骤:
1)获取类的字节码(Class)对象
2)获取类中的成员
3)使用类中的成员
获取字节码(Class)对象的方式:
1) Class.forName(全类名) 多用于配置文件
2) 类名.class 多用于参数传递
3) 对象名.getClass() 多用于对象的获取字节码的方式
结论:三种方式获取的字节码(Class)对象都是同一个
1.Class类对象的功能:
常用方法:
static Class<?> forName(String className)
String getName() 获取全类名
T newInstance()
获取功能:
1.获取成员变量
Field getField(String name):
获取指定名称的public修饰的成员变量
Field[] getFields():
获取所有的public修饰的成员变量
Field getDeclaredField(String name):
获取指定名称的所有权限的成员变量,即使是私有的都能获取
Field[] getDeclaredFields():
获取所有的不限权限修饰的成员变量,即使是私有的都能获取
2.获取构造方法:
Constructor<T> getConstructor(Class<?>... parameterTypes) :
获取指定的public修饰的构造方法
Constructor<?>[] getConstructors() :
获取所有public修饰的构造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) :
获取指定的所有权限的构造方法,即使是私有的都能获取
Constructor<?>[] getDeclaredConstructors() :
获取所有的不限权限修饰的构造方法,即使是私有的都能获取
3.获取成员方法:
Method getMethod(String name, Class<?>... parameterTypes):
获取指定名称的public修饰的成员方法
Method[] getMethods():
获取所有public修饰的成员方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes):
获取指定名称的不限权限的成员方法,即使是私有的都能获取
Method[] getDeclaredMethods():
获取所有的不限权限的成员方法
4.获取全类名
tring getName()
2.File类(成员变量)
1. 设置值:
void set(Object obj, Object value) 给指定的对象的成员变量赋值为value
2. 获取值:
get(Object obj) 获取指定对象的成员变量的值
3. 忽略访问权限修饰符的安全检查
setAccessible(true):暴力反射,设置可以访问
获取成员变量
借助Class类中的getFiled方法
使用成员变量(赋值和取值)
步骤:
(1)获取Class对象
(2)获取成员变量的Filed对象
(3)如果获取的成员变量是非public,必须进行暴力反射,使用setAccessible方法
(4)调用Field类中的方法进行赋值和取值,分别使用set和get方法
3.onstructor类(构造方法)
常用方法
T newInstance(Object... initargs)
获取构造方法
借助Class类中的getConstructor方法
使用(创建对象)
使用空参构造
方式一:
(1)获取Class对象
(2)通过Class对象中的getConstructor()方法获取构造方法的Constructor对象
(3)通过Constructor对象的newInstance()
方式二:
(1)获取Class对象
(2)通过Class对象调用newInstance()方法创建对象
使用有参构造
(1)获取Class对象
(2)通过Class对象中的getConstructor(构造方法参数的类型)方法获取构造方法的Constructor对象
(3)通过Constructor对象的newInstance(实参)
4.Method类(成员方法)
常用方法:
Object invoke(Object obj, Object... args)
获取成员方法:
借助Class类中的getMethod方法
使用成员方法(调用)
步骤:
(1)获取Class对象
(2)通过Class类中的getMethod方法获取成员方法的Method对象
(3)通过Method对象中的invoke方法使方法执行起来
三、注解
概念:说明程序的。给计算机看的
比较:注释:用文字描述程序的。给程序员看的
格式: @注解名
注解的作用
(1)用于生成文档,一般放在文档注释(/**注释内容*/)中使用,比如 @author , @param , @since , @version
(2)编译检查,比如 @Override , @FunctionalInterface
(3)代码分析
JDK内置注解的使用
@Override :检测被该注解标注的方法是否是继承自父类(接口)的
@Deprecated :该注解标注的内容,表示已过时
@SuppressWarnings :压制警告
一般传递参数all @SuppressWarnings("all")
自定义注解
格式:
java
元注解
public @interface 注解名{
属性列表;
}
注解的本质:注解就是一个接口,默认继承了 java.lang.annotation.Annotation
注解的属性:注解中定义的抽象成员方法
元注解:也是注解,它用于描述注解的
@Target :描述注解能够作用的位置
ElementType 取值:
TYPE :可以作用于类上
METHOD :可以作用于方法上
FIELD :可以作用于成员变量上
@Retention :描述注解被保留的阶段
@Retention(RetentionPolicy.RUNTIME) :当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
@Documented :描述注解是否被抽取到api文档中
@Inherited :描述注解是否被子类继承