Java基础14

Annotation注解

理解:是从jdk5.0开始引入,,以“@注解名”在代码中存在

> Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。

   还可以添加一些参数值,这些信息被保存在Annotation的"name = value"对中。

> 注解可以在类编译、运行时进行加载,体现不同的功能。

应用场景

示例1:生成文档相关的注解

示例2:在编译时进行格式检查(JDK内置的三个基本注解)

示例3:跟踪代码依赖性,实现替代配置文件功能

Java基础涉及到的三个常用注释

'@Override':限定重写父类方法,该注解只能用于方法

'@Deprecated':用于表示所修饰的元素(类、方法)已过时。通常是因为所修饰的结构危险或存在更好的选择。

'@SuppressWarnings':抑制编译器警告

复制代码
public class AnnotationTest {
    public static void main(String[] args) {
        Person p1 = new Student();
        p1.eat();

        Person p2 = new Person();
        Person p3 = new Person("Tom");  //划线表示这个构造器不推荐使用,认为是过时的

        @SuppressWarnings("unused") int num = 10; // 抑制编译器警告
    }
}

class Person{
    String name;
    int age;

    public Person(){
    }

    @Deprecated  // 表示所修饰的元素(类、方法)已过时,通常是因为所修饰的结构危险或存在更好的选择。
    public Person(String name){
        this.name = name;
    }

    public void eat(){
        System.out.println("人吃饭");
    }
}

class Student extends Person{
    @Override // 限定重写父类方法,该注解只能用于方法
    public void eat(){
        System.out.println("学生吃饭");
    }

    public void walk(){
        System.out.println("学生走路");
    }
}
复制代码

自定义注解

以@SuppressWarnings为参照,进行定义即可。

前面必须使用@interface

public @interface MyAnnotation {
    String value()default "hello"; // 如果没写default "hello"  则用这个注解括号内必须有一个value值,否则报错!
}
复制代码
@MyAnnotation(value = "class")
class Person{
    String name;
    int age;

    @MyAnnotation()
    public Person(){
    }
}
复制代码

元注解

对现有的注解进行解释说明的注解

四个元注解

① @Target:用于描述枚举类型ElementType的10个常量对象来指定  表示在哪些结构内可以使用

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

② @Retention:用于描述注解的生命周期

(即:被描述的注解在它所修饰的类中可以被保留到何时).

Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中.    

注意:生命周期长度 SOURCE < CLASS < RUNTIME 

public enum RetentionPolicy {
 
    SOURCE,    // 源文件保留
    CLASS,       // 编译期保留,默认值
    RUNTIME   // 运行期保留,可通过反射去获取注解信息
}

③ @Documented:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

④ @Inherited:允许子类继承父类中的注解

JUnit单元测试

测试分类:

黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。

 白盒测试:需要写代码。关注程序具体的执行流程。

 JUnit单元测试的使用

需要导入的jar包:

junit-4.12.jar

hamcrest-core-1.3.jar

示例:

 正确的单元测试方法要求

- 所在的类必须是public的,非抽象的,包含唯一的无参构造器。

- @Test标记的方法本身必须是public,非抽象的,非静态的,void无返回值,()无参数的。

默认情况下,单元测试方法中使用Scanner失效,解决办法:

 如何定义自己的模板:

 包装类

为什么要使用包装类:

为了使得基本数据类型的变量具备引用数据类型变量的相关特性(比如:封装性、继承性、多态性),我们给各个基本数据类型的变量都提供了对应的包装类。

基本数据类型对应的包装类

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

为什么需要转换

  • 一方面,有些场景下,需要使用基本数据类型对应的包装类的对象。此时需要将基本的数据类型的变量转换为包装类的对象。比如:ArrayList的add(Object obj);Object类的equals(Object obj)
  • 对于包装类来讲,既然我们使用的是对象,那么对象是不能进行+ - * / 等运算的。为了能够进行这些运算,就需要将包装类的对象转换为基本数据类型的变量。

基本数据类型-->包装类:

① 使用包装类的构造器

② 建议调用包装类的valueOf(XXX XX)

复制代码
public class WrapperTest<i2> {
    /**
     基本数据类型--->包装类: ① 使用包装类的构造器   ② 建议调用包装类的valueOf(XXX XX)
     */
    @Test
    public void test1(){
        // 使用包装类的构造器
        int i1 = 10;
        Integer ii1 = new Integer(i1);
        System.out.println(ii1.toString());

        // 推荐使用
        int i2 = 10;
        Integer ii2 = Integer.valueOf(i2);

        Boolean b2 = Boolean.valueOf(true);

        Float ff1 = Float.valueOf(23.4F);
    }
}
复制代码

 光标放在分号之前,Alt + Enter。

初始化值的变化

原来使用基本数据类型变量的位置,改成包装类以后,对于成员变量来说,其默认值变化了。

复制代码
public class WrapperTest<i2> {
    @Test
    public void test1(){
        Account account = new Account();
        System.out.println(account.isFlag1);   //false   基本数据类型
        System.out.println(account.isFlag2);   //null   对象
        System.out.println(account.balance1);    //0.0   基本数据类
        System.out.println(account.balance2);   //null   对象
    }
}

class Account{
     boolean isFlag1;
     Boolean isFlag2;
     double balance1;
     Double balance2;
}
复制代码

 包装类-->基本数据类型

复制代码
public class WrapperTest<i2> {
    /**
     包装类 ---> 基本数据类型 : 调用包装类的xxxValue()
     */
    @Test
    public void test1(){
        // 推荐使用
        int i2 = 10;
        Integer ii2 = Integer.valueOf(i2);

        Boolean b2 = Boolean.valueOf(true);

        Float ff1 = Float.valueOf(23.4F);

        // 包装类 ---> 基本数据类型 : 调用包装类的xxxValue()
        int i = ii2.intValue();
        boolean b = b2.booleanValue();
        float f = ff1.floatValue();
    }
}
复制代码

 jdk5.0新特性

基本数据类型--->包装类: 装箱

包装类--->基本数据类型:拆箱

jdk5.0新特性:自动装箱、自动拆箱

复制代码
public class WrapperTest<i2> {
    /**
     * jdk5.0新特性: 自动装箱、自动拆箱
     */
    @Test
    public void test1(){
        // 自动装箱: 基本数据类型 ---> 包装类
        int i1 = 10;
        Integer ii1 = i1;  // 自动装箱
        System.out.println(ii1.toString());
        Integer ii2 = i1 + 1;  // 自动装箱
        Boolean bb1 = true;   // 自动装箱


        // 自动拆箱:包装类 ---> 基本数据类型
        int i2 = ii1;  // 自动拆箱
        boolean b2 = bb1;  // 自动拆箱
    }
}
复制代码

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

基本数据类型、包装类 ---> String类型:

① 调用String 的重载的静态方法valueOf(xxx xx)

② 基本数据类型的变量 + " "

String类型 ---> 基本数据类型、包装类:

调用包装类的静态方法:parseXxx()

复制代码
public class WrapperTest<i2> {
    /**
     * 基本数据类型、包装类 ---> String类型 : 方式一 调用String的重载的静态方法valueOf(XXX XX); 方式二 基本数据类型的变量 + ""
     * String类型 ---> 基本数据类型、包装类 : 调用包装类的静态方法: parseXxx()
     */
    @Test
    public void test1(){
        // 方式一
        int i1 = 10;
        String str1 = String.valueOf(i1);
        System.out.println(str1);  // "10"

        boolean b1 = true;
        Boolean b2 = b1;
        String str2 = String.valueOf(b2);

        // 方式二
        String str3 = i1 + "";
        String str4 = b1 + "";
    }

    @Test
    public void test2(){
        String str = "123";
        int i = Integer.parseInt(str);
        System.out.println(i + 10);
        //特别的错误情况
        String str2 = "123s";
        int i2 = Integer.parseInt(str2);  // 报错:java.lang.NumberFormatException: For input string: "123s"
    }
}
复制代码

练习

利用vector代替数组处理,从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。

提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。

1.创建Vector对象: Vector v = new Vectoe();

2.给向量添加元素:v.addElement(Object obj);   //obj必须是对象

3.取出向量中的元素:Object obj = v.elementAt(0); 注意第一个元素的下标是0,返回值是Object类型的。

4.计算向量的长度:v.size();

5.若与最高分相差10分内:A等;20分内:B等;30分内:C等;其他:D等

复制代码
import java.util.Scanner;
import java.util.Vector;

public class Test {
    public static void main(String[] args) {
        Vector v = new Vector();
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入成绩(负数退出):");
        int score = 0;
        int max_score = 0;
        while (true){
            score = scan.nextInt();
            if (score < 0){
                break;
            }
            Integer i = score;
            v.addElement(i);
            if(max_score < score){
                max_score = score;
            }
        }
        for (int i = 0 ; i < v.size(); i++){
            Object obj = v.elementAt(i);
            //一般做法:
//            Integer ii = (Integer) obj;
//            int ii1 = Integer.valueOf(ii);
            // 自动拆箱
            int ii1 = (Integer) obj;
            if (max_score - ii1 < 10){
                System.out.println("A等");
            }else if(max_score - ii1 < 20){
                System.out.println("B等");
            }else if (max_score - ii1 < 30){
                System.out.println("C等");
            }else {
                System.out.println("D等");
            }
        }
        scan.close();
    }
}
复制代码

Java包装类的缓存机制

在 Java 中,每个基本数据类型都有对应的包装类,如 Integer、Double 等。

Java 包装类缓存机制指的是,在某些情况下,Java 会对一定范围内的包装类对象进行缓存,以提高性能和节省内存空间。

为什么需要包装类缓存机制:

使用包装类可以使基本数据类型具备面向对象的特性,同时也方便了在集合类中存储基本数据类型。然而,由于包装类是对象,相比于基本数据类型,它们占用更多的内存空间。

为了解决这个问题,Java 引入了包装类缓存机制,通过缓存一定范围内的包装类对象,减少重复创建对象的开销,从而提高性能和节省内存空间。

实现原理

Java 包装类缓存机制是通过静态成员变量来实现的。在 Integer、Long、Short、Byte、Character 这五个包装类中,定义了一个静态数组 cache[],用于缓存常用的数值。

  • Integer 类:默认缓存了-128 到 127 之间的整数。
  • Long 类:默认缓存了-128 到 127 之间的长整数。
  • Short 类:默认缓存了-128 到 127 之间的短整数。
  • Byte 类:默认缓存了-128 到 127 之间的字节。
  • Character 类:默认缓存了 0 到 127 之间的字符。

当使用 valueOf()方法创建包装类对象时,会先检查该值是否在缓存范围内。如果是,则直接返回缓存中的对象;否则,创建一个新的对象并放入缓存中。

复制代码
public class Test {
    public static void main(String[] args) {
        Integer m = 1;
        Integer n = 1;
        System.out.println(m == n);  // true  属于缓存范围内,所以位置相同
        
        Integer x = 128;
        Integer y = 128;
        System.out.println(x == y);  // false 128超过缓存范围,创建了新对象
        
        Integer i1 = 10;
        Double d1 = 10.2;
        System.out.println(i1 == d1);  // 编译报错,因为==只可以比较妖媚相同类型,要么属于子父类关系,然而Integer和Double并列,并不能比较
        
        Integer m1 = 1000;
        double n1 = 1000;
        System.out.println(m == n);  // true 当有基本数据类型(double)和引用数据类型(Integer)会将引用数据类型拆箱比较
        
        Integer x1 = 1000;
        int y1 = 1000;
        System.out.println(x1 == y1);  // true
    }
}
复制代码

 

posted on   gjwqz  阅读(2)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示