注解
概述
注解是从JDK5.0开始引入的,以@注释名
在代码中存在,还可以添加一些参数值,注解可以在类编译、运行时进行加载,来体现不同的功能。比如:
java
@Override
@Deprecated
...
Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的name=value
对中。
注解也可以看做是一种注释,通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。同时注解不同于单行注释和多行注释,对于单行注释和多行注释是给程序员看的,而注解是可以被编译器或其他程序读取的一种注释,程序还可以根据注解的不同做出相应的处理,所以注解是插入到代码中以便于工具可以对它们进行处理的标签。
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能、忽略警告等。未来的开发模式都是基于注解的,注解是一种趋势,一定程度上可以说:框架=注解+反射+设计模式
。
常见的Annotation作用
使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用,用于修饰它支持的程序元素。
示例:
java
//生成文档相关的注解
@author //标明开发该类模块的作者,多个作者之间使用,分割
@version //标明该类模块的版本
@see //参考转向,也就是相关主题
@since //从哪个版本开始增加的
@param //对方法中某参数的说明,如果没有参数就不能写
@return //对方法返回值的说明,如果方法的返回值类型是void就不能写
@exception //对方法可能抛出的异常进行说明,如果方法没有用throws显式抛出的异常就不能写
//编译时进行格式检查,JDK内置的三个基本注解
@Override //限定重写父类方法,该注解只用于方法
@Deprecated //用于表示所修饰的元素(类,方法等)已过时,通常是因为所修饰的结构危险或存在更好的选择
@SuppressWarnings //抑制编译器警告
//跟踪代码依赖性,实现替代配置文件功能
@WebServlet("/login") //Servlet3.0提供了注解,使得不再需要在web.xml文件中进行Servlet的部署
@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITIED,readOnly=false,timeout=3) //Spring框架中关于事物的管理
三个最基本的注解
- @Override
- 用于检测被标记的方法为有效的重写方法,如果不是则报编译错误
- 只能标记在方法上
- 它会被编译器程序读取
- @Deprecated
- 用于表示被标记的数据已经过时,不推荐使用
- 可以用于修饰属性、方法、构造、类、包、局部变量、参数
- 它会被编译器程序读取
- @SuppressWarnings
- 抑制编译警告,当不希望看到警告信息的时候,可以使用SuppressWarnings注解来抑制警告信息
- 可以用于修饰类、属性、方法、构造、局部变量、参数
- 它会被编译器程序读取
- 可以指定的警告类型有以下几种(了解即可):
shell
all,抑制所有警告
boxing,抑制与封装/拆装作业相关的警告
cast,抑制与强制转型作业相关的警告
dep-ann,抑制与淘汰注释相关的警告
deprecation,抑制与淘汰的相关警告
fallthrough,抑制与 switch 陈述式中遗漏 break 相关的警告
finally,抑制与未传回 finally 区块相关的警告
hiding,抑制与隐藏变数的区域变数相关的警告
incomplete-switch,抑制与 switch 陈述式(enum case)中遗漏项目相关的警告
javadoc,抑制与 javadoc
nls,抑制与非 nls 字串文字相关的警告
null,抑制与空值分析相关的警告
rawtypes,抑制与使用 raw 类型相关的警告
resource,抑制与使用 Closeable 类型的资源相关的警告
restriction,抑制与使用不建议或禁止参照相关的警告
serial,抑制与可序列化的类别遗漏 serialVersionUID 栏位相关的警告
static-access,抑制与静态存取不正确相关的警告
static-method,抑制与可能宣告为 static 的方法相关的警告
super,抑制与置换方法相关但不含 super 呼叫的警告
synthetic-access,抑制与内部类别的存取未最佳化相关的警告
sync-override,抑制因为置换同步方法而遗漏同步化的警告
unchecked,抑制与未检查的作业相关的警告
unqualified-field-access,抑制与栏位存取不合格相关的警告
unused,抑制与未用的程式码及停用的程式码相关的警告
元注解
JDK1.5在java.lang.annotation
包定义了4个标准的meta-annotation
类型,它们被用来提供对其他annotation类型做说明。
- @Target:用于描述注解的使用范围。
- 可以通过枚举类型ElementType的10个常量对象来指定
- TYPE,METHOD,CONSTRUCTOR,PACKAGE......
- @Retention:用于描述注解的生命周期。
- 可以通过枚举类型RetentionPolicy的3个常量对象来指定
- SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
- 唯有RUNTIME阶段才能被反射读取到
- @Documented:表明这个注解应该被javadoc工具记录。
- @Inherited:允许子类继承父类中的注解。
示例:
java
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
自定义注解的使用
一个完整的注解应该包含三个部分,声明、使用和读取。
声明自定义注解:
java
【元注解】
【修饰符】 @interface 注解名{
【成员列表】
}
- 自定义注解可以通过四个元注解(@Retention、@Target、@Inherited、@Documented)分别说明它的生命周期,使用位置,是否被继承,是否被生成到API文档中
- Annotation的成员在Annotation定义中以无参数有返回值的抽象方法的形式来声明,又称为配置参数,返回值类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组
- 可以使用default关键字为抽象方法指定默认返回值
- 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值,格式是方法名=返回值,如果只有一个抽象方法需要赋值,且方法名为value,可以省略value=,所以如果注解只有一个抽象方法成员,建议使用方法名value
示例:
java
import java.lang.annotation.*;
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
使用自定义注解:
java
@Table("t_stu")
public class Student {
@Column(columnName = "sid",columnType = "int")
private int id;
@Column(columnName = "sname",columnType = "varchar(20)")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
读取和处理自定义注解:
自定义注解必须配上注解的信息处理流程才有意义,自己定义的注解,只能使用反射的代码进行读取,所以自定义注解的声明周期必须是RetentionPolicy.RUNTIME。
包装类
概述
Java提供了两个类型系统,基本类型和引用类型,使用基本类型在于效率,然而当要使用只针对对象设计的API或新特性时就需要使用包装类。
Java针对八种基本数据类型定义了相应的引用类型:包装类(封装类)。有了类的特点,就可以调用类中的方法,Java才是真正的面向对象。
基本数据类型 | 包装类 | 父类 |
---|---|---|
byte | Byte | Number |
short | Short | Number |
int | Integer | Number |
long | Long | Number |
float | Float | Number |
double | Double | Number |
boolean | Boolean | |
char | Character |
封装以后的内存结构对比,以下面代码为例:
java
public static void main(String[] args){
int num = 520;
Integer obj = new Integer(520);
}
内存结构对比:
自定义包装类
示例:
java
public class MyInteger {
int value;
public MyInteger() {
}
public MyInteger(int value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
包装类与基本数据类型间的转换
装箱:
把基本数据类型转为包装类对象,转为包装类的对象是为了使用专门为对象设计的API和特性。
java
Integer obj1 = new Integer(4); //使用构造函数
Float f = new Float("4.56");
Long l = new Long("abcd"); //NumberFormatException
Integer obj2 = Integer.valueOf(4); //使用包装类中的valueOf方法
拆箱:
把包装类对象拆为基本数据类型,转为基本数据类型,一般是因为需要运算,Java中的大多数运算符是为基本数据类型设计的,比如比较运算符、算术运算符等。
java
Integer obj = new Integer(4);
int num1 = obj.intValue();
自动装箱和拆箱:
由于经常要做基本类型和包装类之间的转换,从JDK5.0开始基本类型和包装类的装箱、拆箱动作可以自动完成,例如:
java
Integer i = 4; //自动装箱,相当于Integer i = Integer.valueOf(4)
i = i + 5; //等号右边将i对象转成基本数值(自动拆箱),相当于i.intValue() + 5
//加法运算完成后,再次装箱,把基本数值转成对象
注意: 只能与自己对应的类型之间才能实现自动装箱和拆箱。
基本数据类型、包装类与字符串间的转换
基本数据类型转为字符串:
java
//调用字符串重载的valueOf()方法
int a = 10;
String str = String.valueOf(a);
//更直接的方式
int a = 10;
String str = a + "";
字符串转为基本数据类型:
java
//除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型,例如:
public static int parseInt(String s) //将字符串参数转换为对应的int基本类型
public static long parseLong(String s) //将字符串参数转换为对应的long基本类型
public static double parseDouble(String s) //将字符串参数转换为对应的double基本类型
//字符串转为包装类,然后可以自动拆箱为基本数据类型
public static Integer valueOf(String s) //将字符串参数转换为对应的Integer包装类,然后可以自动拆箱为int基本类型
public static Long valueOf(String s) //将字符串参数转换为对应的Long包装类,然后可以自动拆箱为long基本类型
public static Double valueOf(String s) //将字符串参数转换为对应的Double包装类,然后可以自动拆箱为double基本类型
//通过包装类的构造器实现
int a = Integer.parseInt("整数的字符串");
double d = Double.parseDouble("小数的字符串");
boolean b = Boolean.parseBoolean("true或false");
int a = Integer.valueOf("整数的字符串");
double d = Double.valueOf("小数的字符串");
boolean b = Boolean.valueOf("true或false");
int i = new Integer("12");
小结:
包装类的其他API
数据类型的最大最小值:
java
Integer.MAX_VALUE和Integer.MIN_VALUE
Long.MAX_VALUE和Long.MIN_VALUE
Double.MAX_VALUE和Double.MIN_VALUE
字符转大小写:
java
Character.toUpperCase('x');
Character.toLowerCase('X');
整数转进制:
java
Integer.toBinaryString(int i)
Integer.toHexString(int i)
Integer.toOctalString(int i)
比较的方法:
java
Double.compare(double d1, double d2)
Integer.compare(int x, int y)
包装类对象的特点
- 包装类缓存对象:
包装类 | 缓存对象 |
---|---|
Byte | -128~127 |
Short | -128~127 |
Integer | -128~127 |
Long | -128~127 |
Float | 没有 |
Double | 没有 |
Character | 0~127 |
Boolean | true和false |
java
Integer a = 1;
Integer b = 1;
System.out.println(a == b); //true
Integer i = 128;
Integer j = 128;
System.out.println(i == j); //false
Integer m = new Integer(1); //新new的在堆中
Integer n = 1; //这个用的是缓冲的常量对象,在方法区
System.out.println(m == n); //false
Integer x = new Integer(1); //新new的在堆中
Integer y = new Integer(1); //另一个新new的在堆中
System.out.println(x == y); //false
Double d1 = 1.0;
Double d2 = 1.0;
System.out.println(d1==d2); //false 比较地址,没有缓存对象,每一个都是新new的
- 类型转换问题:
java
Integer i = 1000;
double j = 1000;
System.out.println(i==j); //true,会先将i自动拆箱为int,然后根据基本数据类型"自动类型转换"规则,转为double比较
Integer i = 1000;
int j = 1000;
System.out.println(i==j); //true,会自动拆箱,按照基本数据类型进行比较
Integer i = 1;
Double d = 1.0
System.out.println(i==d); //编译报错
- 包装类对象不可变:
java
public class Test {
public static void main(String[] args) {
int i = 1;
Integer j = new Integer(2);
Circle c = new Circle();
change(i,j,c);
System.out.println("i = " + i); //1
System.out.println("j = " + j); //2
System.out.println("c.radius = " + c.radius); //10.0
}
//方法的参数传递机制:基本数据类型的形参修改完全不影响实参;引用数据类型通过形参修改对象的属性值,会影响实参的属性值;这类Integer等包装类对象是"不可变"对象,即一旦修改就是新对象,和实参就无关了
public static void change(int a ,Integer b,Circle c ){
a += 10;
//b += 10;//等价于 b = new Integer(b+10);
c.radius += 10;
//c = new Circle();
//c.radius+=10;
}
}
class Circle{
double radius;
}
今天的内容就到这里,喜欢的话点个关注吧,下篇见!