全面掌握Java的注解和包装类

注解

概述

注解是从JDK5.0开始引入的,以@注释名在代码中存在,还可以添加一些参数值,注解可以在类编译、运行时进行加载,来体现不同的功能。比如:

@Override
@Deprecated
...

Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的name=value对中。

注解也可以看做是一种注释,通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。同时注解不同于单行注释和多行注释,对于单行注释和多行注释是给程序员看的,而注解是可以被编译器或其他程序读取的一种注释,程序还可以根据注解的不同做出相应的处理,所以注解是插入到代码中以便于工具可以对它们进行处理的标签。

在JavaSE中,注解的使用目的比较简单,例如标记过时的功能、忽略警告等。未来的开发模式都是基于注解的,注解是一种趋势,一定程度上可以说:框架=注解+反射+设计模式

常见的Annotation作用

使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用,用于修饰它支持的程序元素。

示例:

//生成文档相关的注解
@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框架中关于事物的管理

三个最基本的注解

  1. @Override
    • 用于检测被标记的方法为有效的重写方法,如果不是则报编译错误
    • 只能标记在方法上
    • 它会被编译器程序读取
  2. @Deprecated
    • 用于表示被标记的数据已经过时,不推荐使用
    • 可以用于修饰属性、方法、构造、类、包、局部变量、参数
    • 它会被编译器程序读取
  3. @SuppressWarnings
    • 抑制编译警告,当不希望看到警告信息的时候,可以使用SuppressWarnings注解来抑制警告信息
    • 可以用于修饰类、属性、方法、构造、局部变量、参数
    • 它会被编译器程序读取
    • 可以指定的警告类型有以下几种(了解即可):
      • 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类型做说明。

  1. @Target:用于描述注解的使用范围。
    • 可以通过枚举类型ElementType的10个常量对象来指定
    • TYPE,METHOD,CONSTRUCTOR,PACKAGE…
  2. @Retention:用于描述注解的生命周期。
    • 可以通过枚举类型RetentionPolicy的3个常量对象来指定
    • SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
    • 唯有RUNTIME阶段才能被反射读取到
  3. @Documented:表明这个注解应该被javadoc工具记录。
  4. @Inherited:允许子类继承父类中的注解。

示例:

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 {
}

自定义注解的使用

一个完整的注解应该包含三个部分,声明、使用和读取。

声明自定义注解:

【元注解】
【修饰符】 @interface 注解名{
    【成员列表】
}
  • 自定义注解可以通过四个元注解(@Retention、@Target、@Inherited、@Documented)分别说明它的生命周期,使用位置,是否被继承,是否被生成到API文档中
  • Annotation的成员在Annotation定义中以无参数有返回值的抽象方法的形式来声明,又称为配置参数,返回值类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组
  • 可以使用default关键字为抽象方法指定默认返回值
  • 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值,格式是方法名=返回值,如果只有一个抽象方法需要赋值,且方法名为value,可以省略value=,所以如果注解只有一个抽象方法成员,建议使用方法名value

示例:

import java.lang.annotation.*;
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}

使用自定义注解:

@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才是真正的面向对象。

基本数据类型包装类父类
byteByteNumber
shortShortNumber
intIntegerNumber
longLongNumber
floatFloatNumber
doubleDoubleNumber
booleanBoolean
charCharacter

封装以后的内存结构对比,以下面代码为例:

public static void main(String[] args){
	int num = 520;
	Integer obj = new Integer(520);
}

内存结构对比:

自定义包装类

示例:

public class MyInteger {
    int value;
    public MyInteger() {
    }
    public MyInteger(int value) {
        this.value = value;
    }
    @Override
    public String toString() {
        return String.valueOf(value);
    }
}

包装类与基本数据类型间的转换

装箱:

把基本数据类型转为包装类对象,转为包装类的对象是为了使用专门为对象设计的API和特性。

Integer obj1 = new Integer(4); //使用构造函数
Float f = new Float(4.56);
Long l = new Long(“abcd”); //NumberFormatException
Integer obj2 = Integer.valueOf(4); //使用包装类中的valueOf方法

拆箱:

把包装类对象拆为基本数据类型,转为基本数据类型,一般是因为需要运算,Java中的大多数运算符是为基本数据类型设计的,比如比较运算符、算术运算符等。

Integer obj = new Integer(4);
int num1 = obj.intValue();

自动装箱和拆箱:

由于经常要做基本类型和包装类之间的转换,从JDK5.0开始基本类型和包装类的装箱、拆箱动作可以自动完成,例如:

Integer i = 4; //自动装箱,相当于Integer i = Integer.valueOf(4)
i = i + 5; //等号右边将i对象转成基本数值(自动拆箱),相当于i.intValue() + 5
//加法运算完成后,再次装箱,把基本数值转成对象

注意:
只能与自己对应的类型之间才能实现自动装箱和拆箱。

基本数据类型、包装类与字符串间的转换

基本数据类型转为字符串:

//调用字符串重载的valueOf()方法
int a = 10;
String str = String.valueOf(a);
//更直接的方式
int a = 10;
String str = a + "";

字符串转为基本数据类型:

//除了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

数据类型的最大最小值:

Integer.MAX_VALUEInteger.MIN_VALUE
Long.MAX_VALUELong.MIN_VALUE
Double.MAX_VALUEDouble.MIN_VALUE

字符转大小写:

Character.toUpperCase('x');
Character.toLowerCase('X');

整数转进制:

Integer.toBinaryString(int i) 
Integer.toHexString(int i)
Integer.toOctalString(int i)

比较的方法:

Double.compare(double d1, double d2)
Integer.compare(int x, int y) 

包装类对象的特点

  1. 包装类缓存对象:
包装类缓存对象
Byte-128~127
Short-128~127
Integer-128~127
Long-128~127
Float没有
Double没有
Character0~127
Booleantrue和false
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的
  1. 类型转换问题:
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); //编译报错
  1. 包装类对象不可变:
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;
}

今天的内容就到这里,喜欢的话点个关注吧,下篇见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农高飞

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值