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

注解

概述

注解是从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框架中关于事物的管理

三个最基本的注解

  1. @Override
    • 用于检测被标记的方法为有效的重写方法,如果不是则报编译错误
    • 只能标记在方法上
    • 它会被编译器程序读取
  2. @Deprecated
    • 用于表示被标记的数据已经过时,不推荐使用
    • 可以用于修饰属性、方法、构造、类、包、局部变量、参数
    • 它会被编译器程序读取
  3. @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类型做说明。

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

包装类对象的特点

  1. 包装类缓存对象:
包装类 缓存对象
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的
  1. 类型转换问题:
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); //编译报错
  1. 包装类对象不可变:
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;
}

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

相关推荐
Elastic 中国社区官方博客2 小时前
使用 Elastic AI Assistant for Search 和 Azure OpenAI 实现从 0 到 60 的转变
大数据·人工智能·elasticsearch·microsoft·搜索引擎·ai·azure
Francek Chen4 小时前
【大数据技术基础 | 实验十二】Hive实验:Hive分区
大数据·数据仓库·hive·hadoop·分布式
吾日三省吾码4 小时前
JVM 性能调优
java
弗拉唐5 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi776 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3436 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀6 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20206 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深6 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++