Java 面向对象编程
目录
- Java 面向对象编程
- 1. 类和对象
- 1.1 引用
- 1.2 方法传参的过程
- 2. 方法和封装
- 2.1 构造方法
- 2.2 方法的重载(overload)
- 2.3 this 关键字
- 2.4 封装
- 3. Static 关键字和继承
- 3.1 Static 关键字
- 3.2 构造块和静态代码块
- 3.3 单例设计模式
- 3.4 继承
- 3.5 方法重写(override)
- 3.6 访问控制
- 4. Final 关键字
- 5. 多态和特殊类
- 5.1 多态
- 5.2 抽象类
- 5.3 接口
- 6. 特殊类
- 6.1 内部类
- 6.2 枚举
- 6.3 注解
1. 类和对象
1.1 引用
new关键字创建该类的对象,这个过程叫做类的实例化
创建对象的本质是在内存的堆区中申请一块存储区域
引用数据类型定义的变量叫做引用变量,引用变量记录对象在堆区中的内存地址信息
可变长参数:返回值类型 方法名(参数的类型... 参数名)参数个数为0~n个
一个方法的形参列表最多只能声明一个可变长参数,并需要放到参数列表的末尾
1.2 方法传参的过程
class m{
int max (int ia, int ib) {}
public static void main(String[] args) {
int a = 5;
int b = 6;
int res = max(a, b);
}
}
- 为main方法的变量a, b,res 分配空间并初始化
- 调用max方法为max方法的形参变量 ia, ib分配空间
- 将实参变量的数值(a,b)赋值到形参变量(ia,ib)的内存空间中
- max()方法执行完毕后返回,形参变量(ia,ib)的空间释放
- main()方法中res变量得到max()方法的返回值
- main()方法结束后释放相关变量的内存空间
方法传参的注意事项
public class ArgumentTest {
void show (int ia) {
System.out.println(ia);
}
public static void main(String[] args) {
ArgumentTest at = new ArgumentTest();
int ib = 10;
at.show(ib);
System.out.println(ib);
}
}
//ia = ib = 10;
public class ArgumentTest {
void show (int ia) {
ia = 200;
System.out.println(ia);
}
public static void main(String[] args) {
ArgumentTest at = new ArgumentTest();
int ib = 10;
at.show(ib);
System.out.println(ib);
}
}
//ia = 200, ib = 10;
基本数据类型的变量作为方法的参数传递时,形参变量数值的改变通常不会影响到实参变量的数值,因为两个变量有各自独立的内存空间
public class ArgumentTest {
void show (int[] arr) {
System.out.println(arr[0]);
}
public static void main(String[] args) {
ArgumentTest at = new ArgumentTest();
int[] arr1 = new int[]{10,20};
at.show(arr1);
System.out.println(arr1[0]);
}
}
//arr[0] = arr1[0] = 10;
public class ArgumentTest {
void show (int[] arr) {
arr[0] = 200;
System.out.println(arr[0]);
}
public static void main(String[] args) {
ArgumentTest at = new ArgumentTest();
int[] arr1 = new int[]{10,20};
at.show(arr1);
System.out.println(arr1[0]);
}
}
//arr[0] = arr1[0] = 200;
引用数据类型变量作为方法的参数传递时,形参变量指向内容的改变会影响到实参变量指向内容的数值,两个变量指向同一块内存空间
public class ArgumentTest {
void show (int[] arr) {
arr = new int[2];
arr[0] = 200;
System.out.println(arr[0]);
}
public static void main(String[] args) {
ArgumentTest at = new ArgumentTest();
int[] arr1 = new int[]{10,20};
at.show(arr1);
System.out.println(arr1[0]);
}
}
//arr[0] = 200, arr1[0] = 10;
引用数据类型变量作为方法的参数传递时,若形参变量改变指向后再改变指定的内容,则通常不会影响实参变量指向内容的改变,因为两个变量指向不同的内存空间
2. 方法和封装
2.1 构造方法
构造方法与类名完全相同并且没有返回值类型(包括viod)
默认/缺省构造方法,当一个类没有定义任何构造方法编译器会自动添加一个无参的构造方法
构造方法的作用是在new关键字创建对象时自动调用构造方法实现成员变量的初始化工作
2.2 方法的重载(overload)
方法名相同,参数列表不相同:
- 参数的个数不同
- 参数的类型不同
- 参数的顺序不同
- 与返回值类型和形参变量名无关
重载的实际意义:记住一个方法名就可以调用不同的版本来实现不同的功能
System.out.print(int i);
System.out.print(String s);
System.out.print(float l);
System.out.print(Double b);
2.3 this 关键字
若在构造方法中出现了this关键字,则代表当前正在构造的对象
若在成员方法中出现了this关键字,则代表当前正在调用的对象
this关键字本质就是当前类类型的引用变量
工作原理
- 构造方法和成员方法访问成员变量时,编译器会加上this.前缀
- 不同的对象调用同一个方法时,由于调用方法的对象不同导致this关键字不同,因此this.访问的结果也不同
使用方式
- 当局部变量名与成员变量名相同时,在方法体中会优先使用局部变量(就 近原则),若希望使用成员变量,则需要在成员变量的前面加上this.的前 缀,明确要求该变量是成员变量
- this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以 作为方法的返回值
class Person {
Person getPerson() {
return this; //返回当前正在调用此方法的对象本身
}
public static void main(String[] args) {
Person p = new Person();
p.getPerson();
}
}
- 在构造方法的第一行可以使用this()的方式来调用本类中的其它构造方法
引用变量注意事项
- 引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不 指向任何对象
- 当某个引用类型变量为null时无法对对象实施访问(因为它没有指向任何 对象)。此时,如果通过引用访问成员变量或调用方法,会产生 NullPointerException 异常
2.4 封装
对成员变量进行处理隐藏成员变量的细节以及保证数值的合理性
封装实现的流程
- 私有化成员变量,使用private关键字修饰
- 提供公有的get和set方法,并进行合理值的判断
- 在构造方法中调用set方法进行合理值判断
3. Static 关键字和继承
3.1 Static 关键字
基本概念
- 修饰成员变量表示静态,成员变量由对象层级提升为类层级,整个类只有一份且被共享,随着类的加载而准备就绪,与是否创建对象无关
- static修饰的成员可使用引用.访问,推荐类名.方式
使用方式
- 在非静态成员方法中既能访问非静态的成员又能访问静态的成员。
- (成员:成员变量 + 成员方法, 静态成员被所有对象共享)
public class Test {
private int cnt = 1;
priavte static int snt = 2;
public void show() {
System.out.println(this.cnt);
System.out.println(this.snt);
}
}
- 在静态成员方法中只能访问静态成员不能访问非静态成员
- (成员:成员变量 + 成员方法, 因为此时可能还没有创建对象)
public class Test {
private int cnt = 1;
priavte static int snt = 2;
public static void show() {
//System.out.println(cnt); cnt隶属于对象层级,静态方法通过类名.方式调用时没有创建对象
System.out.println(snt); // Test.snt
}
}
- 在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用 static关键字修饰。(不能滥用static关键字)
3.2 构造块和静态代码块
构造块:在类体中直接使用{}括起来的代码块。
- 每创建一个对象都会执行一次构造块。
- 当需要在执行构造方法体之前做一些准备工作时,构造快中存储相关代码,比如对成员变量进行统一的初始化
public class BlockTest {
{
System.out.println(1);
}
public BlockTest() {
System.out.println(2);
}
public static void main(String[] args) {
BlockTest bt = new BlockTest();
}
}
//代码输出顺序 1 2
public static void main(String[] args) {
BlockTest bt = new BlockTest();
BlockTest bt1 = new BlockTest();
}
//代码输出顺序 1 2 1 2
静态代码块:使用static关键字修饰的构造块。
- 静态代码块随着类加载时执行一次,先于构造块执行
- 当需要在执行代码块之前随着类的加载做一些准备工作时,则编写代码到静态代码块中,比如加载数据库的驱动包等
public class BlockTest {
{
System.out.println(1);
}
static {
System.out.println(0);
}
public BlockTest() {
System.out.println(2);
}
public static void main(String[] args) {
BlockTest bt = new BlockTest();
}
}
//代码输出顺序 0 1 2
public static void main(String[] args) {
BlockTest bt = new BlockTest();
BlockTest bt1 = new BlockTest();
}
//代码输出顺序 0 1 2 1 2
构造块和静态代码块考点
- 先执行父类的静态代码块,再执行子类的静态代码块
- 执行父类的构造块,执行父类的构造方法体
- 执行子类的构造块,执行子类的构造方法体
public class SuperTest {
{
System.out.println(11);
}
static {
System.out.println(10);
}
public SuperTest(){
System.out.println(12);
}
}
public class SubSuperTest extends SuperTest{
{
System.out.println(21);
}
static {
System.out.println(20);
}
public SuperTest(){
System.out.println(22);
}
}
public static void main(String[] args) {
SubSuperTest ss = new SubSuperTest();
}
//执行的顺序为:10 20 11 12 21 22
3.3 单例设计模式
概念
- 在某些特殊场合中,一个类对外提供且只提供一个对象时,这样的类叫 做单例类,而设计单例的流程和思想叫做单例设计模式
实现流程
- 私有化构造方法,使用private关键词修饰,确保此类在该类外不能new创建对象但可以在本类中new创建对象
- 声明本类类型的引用指向本类类型的对象(new),使用static关键字(将此对象由对象层级提升为类层级,通过类名.方式调用)和 private关键字(私有化创建的对象使得外部没法直接访问,进而防止外部通过类名.的方式将其赋值为null报废引用变量)
- 提供公有的get方法负责将对象返回出去,并使用public关键字(使外部可以调用get方法) static关键字(将此方法提升为类层级)共同 修饰
public class Singleton {
private static Singleton st = new Singleton();//声明本类类型的引用指向本类类型的对象
private Singleton(){} //1.私有化构造方法
public static void getInstance(){
return st;
} //3.提供公有的get方法将对象返回出去
}
public class SingletonTest {
public static void main(String[] args) {
Singleton st = Singleton.getInstance();
}
}
饿汉式和懒汉式
- 饿汉式
public class Singleton {
private static Singleton st = new Singleton();//饿汉式
private Singleton(){}
public static void getInstance(){
return st;
}
}
- 懒汉式 - 调用方法时才创建对象
public class Singleton {
private static Singleton st = null;//懒汉式
private Singleton(){}
public static void getInstance(){
if(st == null){
st = new Singleton();
}
return st;
}
}
3.4 继承
概念
- 当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成 一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需 要编写自己独有特征和行为的机制,叫做继承
- 使用extends关键字,继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件
特点
- 子类不能继承父类的构造方法(构造方法要求方法名和类名相同,继承下来没有意义)和私有方法(私有方法只能在本类中使用),但私有成员变量可以被继承 只是不能直接访问
- 无论使用何种方式构造子类的对象时都会自动调用父类的无参构造方法, 来初始化从父类中继承的成员变量,相当于在构造方法的第一行增加代 码super()的效果
- 使用继承必须满足逻辑关系:子类 is a 父类,也就是不能滥用继承
3.5 方法重写(override)
方法重写的原则:
- 要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许 返回子类类型
- 要求方法的访问权限不能变小,可以相同或者变大
- 要求方法不能抛出更大的异常(异常机制)
@Override
public void show(){
super.show();
...
}
3.6 访问控制
修饰符 | 本类 | 同一个包中的类 | 子类 | 其他类 |
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
默认 | Y | Y | N | N |
private | Y | N | N | N |
4. Final 关键字
概念
- final本意为"最终的、不可改变的",可以修饰类、成员方法以及成员变量
使用方式
- final关键字修饰类体现在该类不能被继承。- 主要用于防止滥用继承,如:java.lang.String类等
- final关键字修饰成员方法体现在该方法不能被重写但可以被继承。- 主要用于防止不经意间造成重写,如:java.text.Dateformat类中format方法等
- final关键字修饰成员变量体现在该变量必须初始化且不能改变 - 主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等
显示初始化:声明变量的同时进行初始化
private final int cnt = 0;
在构造块或者构造方法体中进行初始化
private final int cnt;
{
cnt = 1;
}
or
class FinalTest {
private final int cnt;
public FinalTest() {
cnt = 1;
}
}
常量:public static final关键字共同修饰成员变量,常量的命名规范要求是所有字母都要大写,不同的单词之间采用下划线连
public static final double PI = 3.14;
5. 多态和特殊类
5.1 多态
概念:多态主要指同一种事物表现出来的多种形态
语法格式:父类的引用指向字类的对象 - 父类类型 引用变量名 = new 子类类型();
多态的特点:
- 当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调 用父类独有的方法
- 当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接调用子类独有的方法
- 对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段 调用子类重写的版本(动态绑定)
- 对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本 - 子类中静态方法对于父类中静态方法的"重写"(方法名,参数列表,返回值类型相同)- 不能加@Override - 历史原因,不是真正意义上的重写 - 重写方法为了使用多态 - 多态必须父类类型的引用指向子类类型的对象 - 静态方法与指向的对象没有关系
引用数据类型之间转化 (自动类型转换 和 强制类型转换):
- 自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也 叫做向上转型
- 强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也 叫做向下转型或显式类型转换
- 引用数据类型之间的转换必须发生在父子类之间,否则编译报错
- 若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运 行阶段发生类型转换异常
class Shape {}
class Rect extneds Shape {}
class Circle extends Shape {}
public static void main(String[] args) {
Shape sr = new Rect();
Circle cr = (Circle) sr;
}
- 为了避免上述错误的发生,应该在强转之前进行判断,格式如下: if(引用变量 instanceof 数据类型) 判断引用变量指向的对象是否为后面的数据类型
public static void main(String[] args) {
Shape sr = new Rect();
if(sr instanceof Circle) // 判断sr指向堆区内存中的对象是否为Circle类型,是返回true不是返回false
Circle cr = (Circle) sr;
}
多态使用场合一:通过参数传递形成了多态
多态使用场合二:直接在方法体中使用抽象类的引用指向子类型的对象
5.2 抽象类
抽象方法的概念:不能具体实现的方法并且使用abstract关键字修饰,也就 是没有方法体
格式:访问权限 abstract 返回值类型 方法名(形参列表);
public abstract void cry();
抽象类的概念:不能具体实例化的类并且使用abstract关键字修饰,也就是 不能创建对象
- 抽象类中可以有成员变量、构造方法、成员方法;不能使用new关键字创建对象 - 因为抽象类中可能有抽象方法 - 抽象方法没有方法体 - 调用抽象方法没有意义- 为了避免去调用抽象方法
- 抽象类中可以没有抽象方法,也可以有抽象方法
- 拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是具有 抽象方法并且使用abstract关键字修饰的类
抽象类的实际意义:
- 抽象类的实际意义不在于创建对象而在于被继承
- 当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也 就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式
开发经验:
- 在以后的开发中推荐使用多态的格式,此时父类类型引用直接调用的所 有方法一定是父类中拥有的方法,若以后更换子类时,只需要将new关键 字后面的子类类型修改而其它地方无需改变就可以立即生效,从而提高 了代码的可维护性和可扩展型
- 该方式的缺点就是:父类引用不能直接调用子类独有的方法,若调用则 需要强制类型转换
笔试考点:
- private 和 abstract 关键字不能共同修饰一个方法 - 子类不能继承父类的构造方法和私有方法 - 抽象方法不能被重写就没有意义了
~~private abstract double get();~~
- final 和 abstract 关键字不能共同修饰一个方法 - fianl修饰的方法可以被继承不能被重写 - 抽象方法不能被重写就没有意义了
public final abstract double get();
- static 和 abstract 关键字不能共同修饰一个方法 - static关键字将方法提升到类层级可以通过类名.调用 - 抽象类不能new对象的意义就不存在了
public static abstact double get();
- final 和 abstract 关键字不能共同修饰一个类 - final修饰的不能被继承 - 抽象类没有意义
5.3 接口
基本概念(interface)
- 接口就是一种比抽象类还抽象的类,体现在所有方法都为抽象方法
类和接口之间的关系
名称 | 关键字 | 关系 |
类和类 | extends | 单继承 |
类和接口 | implements | 多实现 |
接口和接口 | extends | 多继承 |
接口和抽象类的主要区别:
- 定义抽象类的关键字是abstract class,而定义接口的关键字是interface
- 继承抽象类的关键字是extends,而实现接口的关键字是implements
- 继承抽象类支持单继承,而实现接口支持多实现
- 抽象类中可以有构造方法,而接口中不可以有构造方法
- 抽象类中可以有成员变量,而接口中只可以有常量
- 抽象类中可以有成员方法,而接口中只可以有抽象方法 - 不考虑新特性- Java 9 开始允许接口出现私有方法
- 抽象类中增加方法时子类可以不用重写(可能是成员方法),而接口中增加方法时实现类需 要重写(Java8以前的版本)- Java 8 之前接口中只能有抽象方法
- 从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法,但非 抽象方法需要使用default关键字修饰,可以灵活选择是否重写
- 从Java9开始增加新特性,接口中允许出现私有方法 - 开发中减少重复的代码及代码的冗余 - 相同的代码提取出来封装成一个方法 - 只为了接口内部使用不需要外部重写 - private
- 接口中不能用protected修饰抽象方法但是抽象类中可以,因为接口要让所有的类(包括非继承的)去实现并不只是子类
- 接口中可以abstract可以自动省略,void show(); 是合法的,但抽象类中不可以省略
- 抽象类中可以有静态方法但不能有静态的抽象方法
6. 特殊类
6.1 内部类
内部类的分类:
- 普通内部类 - 直接将一个类的定义放在另外一个类的类体中
- 静态内部类 - 使用static关键字修饰的内部类,隶属于类层级
- 局部内部类 - 直接将一个类的定义放在方法体的内部时
- 匿名内部类 - 就是指没有名字的内部类
普通内部类的使用方式:
OuterClass oc = new OuterClass();
OuterClass.InnerClass ic = oc.new InnerClass();
- 普通内部类和普通类一样可以定义成员变量、成员方法以及构造方法等
- 普通内部类和普通类一样可以使用final或者abstract关键字修饰
- 普通内部类还可以使用private或protected关键字进行修饰
- 普通内部类需要使用外部类对象来创建对象
- 如果内部类访问外部类中与本类内部同名的成员变量或方法时,需要使用this关键字
public class Outer {
private int cnt = 1;
public class inner {
private int cnt = 2;
public void show(int cnt){
System.out.println("形参cnt"+cnt); //局部优先原则
System.out.println("内部类cnt"+this.cnt);
System.out.println("外部类cnt"+Outer.this.cnt);
}
}
静态内部类的使用方式:
Outer.Inner si = new Outer.Inner();
- 静态内部类不能直接访问外部类的非静态成员
- 静态内部类可以直接创建对象
- 如果静态内部类访问外部类中与本类内同名的成员变量或方法时,需要 使用类名.的方式访问
public class Outer {
private static int cnt = 1;
public static class inner {
private static int cnt = 2;
public void show(int cnt){
System.out.println("形参cnt"+cnt); //局部优先原则
System.out.println("内部类cnt"+Inner.cnt);
System.out.println("外部类cnt"+Outer.cnt);
}
}
局部内部类的使用方式:
- 局部内部类只能在该方法的内部可以使用
- 局部内部类可以在方法体内部直接创建对象
- 局部内部类不能使用访问控制符和static关键字修饰符
- 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内 部类和局部变量的声明周期不同所致
匿名内部类的语法格式:
- 接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 };
public Interface AnonymousInterface {
public abstract void show();
}
AnonymousInterface ait = new AnonymousInterface() {
@Override
public void show(){
....
}
};
//从Java 8 开始提出新特性lamda表达式可以简化上述代码,格式为:(参数列表) -> {方法体}
AnonymousInterface ait = () -> {....};
回调模式:
- 如果一个方法的参数是接口类型,则在调用该方法时, 需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用 到参数对象中所实现的方法(接口中定义的)
开发经验:当接口/类类型的引用作为方法的形参时,实参的传递方式有两种:
- 自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递 - 方法只调用一次时调用完所占内存空间不会释放
- 使用匿名内部类的语法格式得到接口/类类型的引用即可
6.2 枚举
定义
- 使用public static final表示的常量描述较为繁琐,使用enum关键字来定 义枚举类型取代常量,枚举类型是从Java5开始增加的一种引用数据类型
- 枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final关键字共同修饰,因此采用枚举类型.的方式调用
- 枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默 认也是私有的
- 枚举类型要求所有枚举值必须放在枚举类型的最前面
Enum类常用方法
static T[] values () | 返回当前枚举类中所有对象 |
String toString () | 返回当前枚举类对象的名称 |
int ordinal () | 获取枚举对象在枚举类中的索引位置 |
static T valueOf (String str) | 将参数指定的字符串名转为当前枚举类的对象 |
int compareTo (E o) | 比较两个枚举对象在定义时的顺序 |
枚举实现接口的方式:
- 枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写 一个,或者每个对象都重写
public enum DirectionEnum implements EnumInterface{
//使用匿名内部类为每个方法都进行重写
//接口/父类型 引用变量 = new 接口/父类型() {方法的重写};
// public static final Direction UP = new Direction("向上");
UP("向上"){
@Override
public void show() {
}
},DOWN("向下") {
@Override
public void show() {
}
},LEFT("向左") {
@Override
public void show() {
}
},RIGHT("向右") {
@Override
public void show() {
}
};
private final String desc;
private DirectionEnum(String desc){
this.desc = desc;
}
public String getDesc(){
return this.desc;
}
}
6.3 注解
概念
- 注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型
- 注解本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、 以及运行时执行指定的处理
- 可以看作是一种特殊的接口
语法格式
访问修饰符 @interface 注解名称 {
注解成员
}
- 自定义注解自动继承java.lang.annotation.Annotation接口
- 若一个注解中没有任何成员,这样的注解叫做标记注解/标识注解
- 通过@注解名称的方式可以修饰包、类、 成员方法、成员变量、构造方 法、参数、局部变量的声明等
使用方式
- 注解体中只有成员变量没有成员方法,而注解的成员变量以“无形参的方 法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该 成员变量的类型
public @interface MyAnnotation {
public String value(); //声明一个String类型的成员变量,名字为value
}
- 如果注解只有一个参数成员,建议使用参数名为value,而类型只能是八 种基本数据类型、String类型、Class类型、enum类型及Annotation类型
- 使用注解时采用 成员参数名 = 成员参数值, ...
@MyAnnotation(value = "Hello")
puclic class Person {
....
}
- 注解不传值的两种情况 - 注解本身没有任何成员(标记注解) - 注解中成员后使用default赋初始值
public @interface MyAnnotation {
public String value() default "hello";
}
元注解的概念
- 元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面
- 元注解主要有 @Retention、@Documented、@Target、@Inherited、 @Repeatable
@Retention
- @Retention 应用到一个注解上用于说明该注解的的生命周期(有效范围)
- RetentionPolicy.SOURCE 注解只在源码阶段保留(.java 文件中),在编译器进行编译时 它将被丢弃忽视
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加 载到 JVM 中,默认方式
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载 进入到 JVM 中,所以在程序运行时可以获取到它们
@Documented
- 使用javadoc工具可以从程序源代码中抽取类、方法、成员等注释形成一 个和源代码配套的API帮助文档,而该工具抽取时默认不包括注解内容
- @Documented用于指定被该注解将被javadoc工具提取成文档
- 定义为@Documented的注解必须设置Retention值为RUNTIME
@Target
- @Target用于指定被修饰的注解能用于哪些元素的修饰,取值如下
ElementType.ANNOTATION_TYPE | 可以给一个注解进行注解 |
ElementType.CONSTRUCTOR | 可以给构造方法进行注解 |
ElementType.FIELD | 可以给属性(成员变量)进行注解 |
ElementType.LOCAL_VARIABLE | 可以给局部变量进行注解 |
ElementType.METHOD | 可以给方法进行注解 |
ElementType.PACKAGE | 可以给一个包进行注解 |
ElementType.PARAMETER | 可以给一个方法内的参数进行注解 |
ElementType.TYPE | 可以给类型进行注解,比如类、接口、枚举 |
@Inherited
- @Inherited并不是说注解本身可以继承,而是说如果一个超类被该注解标 记过的注解进行注解时,如果子类没有被任何注解应用时,则子类就继 承超类的注解
@Repeatable
- @Repeatable表示自然可重复的含义,从Java8开始增加的新特性
- 从Java8开始对元注解@Target的参数类型ElementType枚举值增加了两个
- 其中ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明 语句中,如:泛型
- 其中ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中
//Java 8 以前
public @interface ManType {
String value();
}
public @interface ManTypes {
ManType[] value();
}
@ManTypes({@ManType(value = "职工"),@ManType(value = "超人")})
public class Man {}
//Java 8 开始
@Repeatable(value = ManTypes.Class)
public @interface ManType {
String value();
}
public @interface ManTypes {
ManType[] value();
}
@ManType(value="职工")
@ManType(value="超人")
public class Man {}
预制注解:预制注解就是Java语言自身提供的注解
@author | 标明开发该类模块的作者,多个作者之间使用,分割 |
@version | 标明该类模块的版本 |
@see | 参考转向,也就是相关主题 |
@since | 从哪个版本开始增加的 |
@param | 对方法中某参数的说明,如果没有参数就不能写 |
@return | 对方法返回值的说明,如果方法的返回值类型是void就不能写 |
@exception | 对方法可能抛出的异常进行说明 |
@Override | 限定重写父类方法, 该注解只能用于方法 |
@Deprecated | 用于表示所修饰的元素(类, 方法等)已过时 |
@SuppressWarnings | 抑制编译器警告 |