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 } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具