10--面向对象OOP--05

2023-12-14 12:21:57

1、代码块

如果成员变量想要初始化的值不是一个硬编码的常量值,而是需要通过复杂的计算或读取文件、或读取运行环境信息等方式才能获取的一些值,该怎么办呢?此时,可以考虑代码块(或初始化块)。

  • 代码块(或初始化块)的作用:对Java类或对象进行初始化
  • 代码块(或初始化块)的分类:
    • 一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块(static block)
    • 没有使用static修饰的,为非静态代码块。

1.1 静态代码块

如果想要为静态变量初始化,可以直接在静态变量的声明后面直接赋值,也可以使用静态代码块。

1.1.1 语法格式

在代码块的前面加static,就是静态代码块。

【修饰符】 class 类{
    static{
        静态代码块
    }
}

1.1.2 静态代码块的特点

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  5. 静态代码块的执行要先于非静态代码块。
  6. 静态代码块随着类的加载而加载,且只执行一次。
public class Chinese {
    //    private static String country = "中国";

    private static String country;
    private String name;

    {
        System.out.println("非静态代码块,country = " + country);
    }

    static {
        country = "中国";
        System.out.println("静态代码块");
    }

    public Chinese(String name) {
        this.name = name;
    }
}
public class TestStaticBlock {
    public static void main(String[] args) {
        Chinese c1 = new Chinese("张三");
        Chinese c2 = new Chinese("李四");
    }
}

1.2 非静态代码块

1.2.1 语法格式

【修饰符】 class 类{
    {
        非静态代码块
    }
    【修饰符】 构造器名(){
        // 实例初始化代码
    }
    【修饰符】 构造器名(参数列表){
        // 实例初始化代码
    }
}

1.2.2 非静态代码块的作用

和构造器一样,也是用于实例变量的初始化等操作。

1.2.3 非静态代码块的意义

如果多个重载的构造器有公共代码,并且这些代码都是先于构造器其他代码执行的,那么可以将这部分代码抽取到非静态代码块中,减少冗余代码。

1.2.4 非静态代码块的执行特点

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 除了调用非静态的结构外,还可以调用静态的变量或方法。
  4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
  5. 每次创建对象的时候,都会执行一次。且先于构造器执行。

1.3 实例变量赋值顺序

2、内部类

2.1 概述

2.1.1 什么是内部类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类(InnerClass),类B则称为外部类(OuterClass)

2.1.2 为什么要声明内部类呢

具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。

总的来说,遵循高内聚、低耦合的面向对象开发原则。

2.1.3 内部类的分类

根据内部类声明的位置(如同变量的分类),我们可以分为:

2.2 成员内部类

2.2.1 概述

如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。

语法格式:

[修饰符] class 外部类{
    [其他修饰符] [static] class 内部类{
    }
}

成员内部类的使用特征,概括来讲有如下两种角色:

  • 成员内部类作为类的成员的角色:
    • 和外部类不同,Inner class还可以声明为private或protected;
    • 可以调用外部类的结构。(注意:在静态内部类中不能使用外部类的非静态成员)
    • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
  • 成员内部类作为类的角色:
    • 可以在内部定义属性、方法、构造器等结构
    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以声明为abstract类 ,因此可以被其它的内部类继承
    • 可以声明为final的,表示不能被继承
    • 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)

注意点:

  1. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
  2. 成员内部类可以直接使用外部类的所有成员,包括私有的数据
  3. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

2.2.2 创建成员内部类对象

  • 实例化静态内部类
外部类名.静态内部类名 变量 = 外部类名.静态内部类名();
变量.非静态方法();
  • 实例化非静态内部类
外部类名 变量1 = new 外部类();
外部类名.非静态内部类名 变量2 = 变量1.new 非静态内部类名();
变量2.非静态方法();

2.2.3 举例

public class TestMemberInnerClass {
    public static void main(String[] args) {
        //创建静态内部类实例,并调用方法
        Outer.StaticInner inner = new Outer.StaticInner();
        inner.inFun();
        //调用静态内部类静态方法
        Outer.StaticInner.inMethod();

        System.out.println("*****************************");

        //创建非静态内部类实例(方式1),并调用方法
        Outer outer = new Outer();
        Outer.NoStaticInner inner1 = outer.new NoStaticInner();
        inner1.inFun();

        //创建非静态内部类实例(方式2)
        Outer.NoStaticInner inner2 = outer.getNoStaticInner();
        inner1.inFun();
    }
}
class Outer{
    private static String a = "外部类的静态a";
    private static String b  = "外部类的静态b";
    private String c = "外部类对象的非静态c";
    private String d = "外部类对象的非静态d";

    static class StaticInner{
        private static String a ="静态内部类的静态a";
        private String c = "静态内部类对象的非静态c";
        public static void inMethod(){
            System.out.println("Inner.a = " + a);
            System.out.println("Outer.a = " + Outer.a);
            System.out.println("b = " + b);
        }
        public void inFun(){
            System.out.println("Inner.inFun");
            System.out.println("Outer.a = " + Outer.a);
            System.out.println("Inner.a = " + a);
            System.out.println("b = " + b);
            System.out.println("c = " + c);
            //            System.out.println("d = " + d);//不能访问外部类的非静态成员
        }
    }

    class NoStaticInner{
        private String a = "非静态内部类对象的非静态a";
        private String c = "非静态内部类对象的非静态c";

        public void inFun(){
            System.out.println("NoStaticInner.inFun");
            System.out.println("Outer.a = " + Outer.a);
            System.out.println("a = " + a);
            System.out.println("b = " + b);
            System.out.println("Outer.c = " + Outer.this.c);
            System.out.println("c = " + c);
            System.out.println("d = " + d);
        }
    }


    public NoStaticInner getNoStaticInner(){
        return new NoStaticInner();
    }
}

2.3 局部内部类

2.3.1 非匿名局部内部类

语法格式:

[修饰符] class 外部类{
    [修饰符] 返回值类型  方法名(形参列表){
    	[final/abstract] class 内部类{
		}
	}    
}
  • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
    • 这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类
  • 和成员内部类不同的是,它前面不能有权限修饰符等
  • 局部内部类如同局部变量一样,有作用域
  • 局部内部类中是否能访问外部类的非静态的成员,取决于所在的方法

举例:

public class TestLocalInner {
    public static void main(String[] args) {
        Outer.outMethod();
        System.out.println("-------------------");

        Outer out = new Outer();
        out.outTest();
        System.out.println("-------------------");

        Runner runner = Outer.getRunner();
        runner.run();

    }
}
class Outer{

    public static void outMethod(){
        System.out.println("Outer.outMethod");
        final String c = "局部变量c";
        class Inner{
            public void inMethod(){
                System.out.println("Inner.inMethod");
                System.out.println(c);
            }
        }

        Inner in = new Inner();
        in.inMethod();
    }

    public void outTest(){
        class Inner{
            public void inMethod1(){
                System.out.println("Inner.inMethod1");
            }
        }

        Inner in = new Inner();
        in.inMethod1();
    }

    public static Runner getRunner(){
        class LocalRunner implements Runner{
            @Override
            public void run() {
                System.out.println("LocalRunner.run");
            }
        }
        return new LocalRunner();
    }

}
interface Runner{
    void run();
}

2.3.2 匿名内部类

因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。

new 父类([实参列表]){
    重写方法...
}
new 父接口(){
    重写方法...
}

举例1:使用匿名内部类的对象直接调用方法:

interface A{
    void a();
}
public class Test{
    public static void main(String[] args){
        new A(){
            @Override
            public void a() {
                System.out.println("aaaa");
            }
        }.a();
    }
}

举例2:通过父类或父接口的变量多态引用匿名内部类的对象

interface A{
    void a();
}
public class Test{
    public static void main(String[] args){
        A obj = new A(){
            @Override
            public void a() {
                System.out.println("aaaa");
            }
        };
        obj.a();
    }
}

举例3:匿名内部类的对象作为实参

interface A{
    void method();
}
public class Test{
    public static void test(A a){
        a.method();
    }

    public static void main(String[] args){
        test(new A(){

            @Override
            public void method() {
                System.out.println("aaaa");
            }
        });
    }   
}

3、枚举类

3.1 概述

  • 枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。
  • 枚举类的例子举不胜举:
    • 星期:Monday(星期一)......Sunday(星期天)
    • 性别:Man(男)、Woman(女)
    • 月份:January(1月)......December(12月)
    • 季节:Spring(春节)......Winter(冬天)
    • 三原色:red(红色)、green(绿色)、blue(蓝色)
    • 线程状态:创建、就绪、运行、阻塞、死亡
  • 若枚举只有一个对象, 则可以作为一种单例模式的实现方式。
  • 枚举类的实现:
    • 在JDK5.0 之前,需要程序员自定义枚举类型。
    • 在JDK5.0 之后,Java支持enum关键字来快速定义枚举类型。

3.2 定义枚举类(JDK5.0 之前)

在JDK5.0 之前如何声明枚举类呢?

  • 私有化类的构造器,保证不能在类的外部创建其对象
  • 在类的内部创建枚举类的实例。声明为:public static final ,对外暴露这些常量对象
  • 对象如果有实例变量,应该声明为private final(建议,不是必须),并在构造器中初始化

示例代码:

package com.suyv.enum01;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-09 09:14
*@Description: JDK5.0之前定义枚举类
*/
public class Season{

    private final String SEASONNAME;    //季节的名称
    private final String SEASONDESC;    //季节的描述

    private Season(String seasonName,String seasonDesc){
        this.SEASONNAME = seasonName;
        this.SEASONDESC = seasonDesc;
    }

    public static final Season SPRING = new Season("春天", "春暖花开");
    public static final Season SUMMER = new Season("夏天", "夏日炎炎");
    public static final Season AUTUMN = new Season("秋天", "秋高气爽");
    public static final Season WINTER = new Season("冬天", "白雪皑皑");

    @Override
    public String toString() {
        return "Season{" +
                "SEASONNAME='" + SEASONNAME + '\'' +
                ", SEASONDESC='" + SEASONDESC + '\'' +
                '}';
    }
}
// 测试枚举类
class SeasonTest{
    public static void main(String[] args) {
        System.out.println(Season.AUTUMN);
    }
}

3.3 定义枚举类(JDK5.0 之后)

3.3.1 enum关键字声明枚举
【修饰符】 enum 枚举类名{
    常量对象列表
}

【修饰符】 enum 枚举类名{
    常量对象列表;

    对象的实例变量列表;
}

举例1:

package com.suyv.enum01;

/**
 * @Author: 憨憨浩浩
 * @CreateTime: 2023-12-09 09:16
 * @Description: 定义星期枚举类
 */
public enum Week {
    MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;
}

// 测试枚举类
class TestEnum {
    public static void main(String[] args) {
        Week w = Week.MONDAY;
        System.out.println(w);
    }
}
3.3.2 enum方式定义的要求和特点
  • 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
  • 列出的实例系统会自动添加 public static final 修饰
  • 如果常量对象列表后面没有其他代码“;”可以省略,否则不可以省略“;”。
  • 编译器给枚举类默认提供的是private的无参构造,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数
  • 如果枚举类需要的是有参构造,需要手动定义,有参构造的private可以省略,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。
  • 枚举类默认继承的是java.lang.Enum类,因此不能再继承其他的类型。
  • JDK5.0 之后switch,提供支持枚举类型,case后面可以写枚举常量名,无需添加枚举类作为限定。

举例2:

package com.suyv.enum01;

/**
 * @Author: 憨憨浩浩
 * @CreateTime: 2023-12-09 09:20
 * @Description: 定义季节枚举类
 */
public enum SeasonEnum {

    SPRING("春天","春风又绿江南岸"),
    SUMMER("夏天","映日荷花别样红"),
    AUTUMN("秋天","秋水共长天一色"),
    WINTER("冬天","窗含西岭千秋雪");

    private final String seasonName;
    private final String seasonDesc;

    private SeasonEnum(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }
    public String getSeasonDesc() {
        return seasonDesc;
    }
}
// 测试季节枚举类
class SeasonEnumTest{
    public static void main(String[] args) {
        SeasonEnum season = SeasonEnum.AUTUMN;
        System.out.println(season);
        System.out.println(season.getSeasonName());
        System.out.println(season.getSeasonDesc());
    }
}

举例3:

package com.suyv.enum01;

/**
 * @Author: 憨憨浩浩
 * @CreateTime: 2023-12-09 09:23
 * @Description: 定义星期枚举类
 */
public enum Week01 {

    MONDAY("星期一"),
    TUESDAY("星期二"),
    WEDNESDAY("星期三"),
    THURSDAY("星期四"),
    FRIDAY("星期五"),
    SATURDAY("星期六"),
    SUNDAY("星期日");

    private final String description;

    private Week01(String description){
        this.description = description;
    }

    @Override
    public String toString() {
        return super.toString() +":"+ description;
    }
}
package com.suyv.enum01;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-09 09:25
*@Description: 测试星期枚举类--以及Switch支持枚举类
*/
public class TestWeek {
    public static void main(String[] args) {
        
        Week week = Week.MONDAY;
        System.out.println(week);

        switch (week){
            case MONDAY:
                System.out.println("怀念周末,困意很浓");break;
            case TUESDAY:
                System.out.println("进入学习状态");break;
            case WEDNESDAY:
                System.out.println("死撑");break;
            case THURSDAY:
                System.out.println("小放松");break;
            case FRIDAY:
                System.out.println("又信心满满");break;
            case SATURDAY:
                System.out.println("开始盼周末,无心学习");break;
            case SUNDAY:
                System.out.println("一觉到下午");break;
        }
    }
}

开发中,当需要定义一组常量时,强烈建议使用枚举类。

3.4 enum中常用方法

String toString()

????????默认返回的是常量名(对象名),可以继续手动重写该方法!

static 枚举类型[] values()

????????返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法

static 枚举类型 valueOf(String name)

????????可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。

String name()

????????得到当前枚举常量的名称。建议优先使用toString()。

int ordinal()

????????返回当前枚举常量的次序号,默认从0开始

举例:

package com.suyv.enum01;

import java.util.Scanner;

/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-09 09:28
*@Description: 枚举类(enum)的常用方法
*/
public class TestEnumMethod {
    public static void main(String[] args) {
        

        //values()
        Week[] values = Week.values();

        for (int i = 0; i < values.length; i++) {
            //ordinal()、name()
            System.out.println((values[i].ordinal()+1) + "->" + values[i].name());
        }
        System.out.println("------------------------");

        Scanner input = new Scanner(System.in);
        System.out.print("请输入星期值:");
        int weekValue = input.nextInt();
        Week week = values[weekValue-1];
        //toString()
        System.out.println(week);

        System.out.print("请输入星期名:");
        String weekName = input.next();
        //valueOf()
        week = Week.valueOf(weekName);
        System.out.println(week);

        input.close();
    }
}

3.5 实现接口的枚举类

  • 和普通 Java 类一样,枚举类可以实现一个或多个接口
  • 若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。
  • 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法

语法:

//1、枚举类可以像普通的类一样,实现接口,并且可以多个,但要求必须实现里面所有的抽象方法!
enum A implements 接口1,接口2{
    //抽象方法的实现
}

//2、如果枚举类的常量可以继续重写抽象方法!
enum A implements 接口1,接口2{
    常量名1(参数){
    //抽象方法的实现或重写
	},
    常量名2(参数){
        //抽象方法的实现或重写
    },
	//...
}

举例:

interface Info{
    void show();
}

//使用enum关键字定义枚举类
enum Season1 implements Info{
    //1. 创建枚举类中的对象,声明在enum枚举类的首位
    SPRING("春天","春暖花开"){
        public void show(){
            System.out.println("春天在哪里?");
        }
    },
    SUMMER("夏天","夏日炎炎"){
        public void show(){
            System.out.println("宁静的夏天");
        }
    },
    AUTUMN("秋天","秋高气爽"){
        public void show(){
            System.out.println("秋天是用来分手的季节");
        }
    },
    WINTER("冬天","白雪皑皑"){
        public void show(){
            System.out.println("2002年的第一场雪");
        }
    };

    //2. 声明每个对象拥有的属性:private final修饰
    private final String SEASON_NAME;
    private final String SEASON_DESC;

    //3. 私有化类的构造器
    private Season1(String seasonName,String seasonDesc){
        this.SEASON_NAME = seasonName;
        this.SEASON_DESC = seasonDesc;
    }

    public String getSEASON_NAME() {
        return SEASON_NAME;
    }

    public String getSEASON_DESC() {
        return SEASON_DESC;
    }
}

4、注解(Annotation)

4.1 注解概述

4.1.1 什么是注解

注解(Annotation)是从JDK5.0开始引入,以“@注解名在代码中存在。例如:

@Override
@Deprecated
@SuppressWarnings(value=”unchecked”)

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

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

4.1.2 注解与注释

注解也可以看做是一种注释,通过使用 Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。但是,注解,不同于单行注释和多行注释。

  • 对于单行注释和多行注释是给程序员看的。
  • 而注解是可以被编译器或其他程序读取的。程序还可以根据注解的不同,做出相应的处理。
4.1.3 注解的重要性

在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。

未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,Struts2有一部分也是基于注解的了。注解是一种趋势,一定程度上可以说:框架 = 注解 + 反射 + 设计模式。

4.2 常见的Annotation作用

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

@author

????????标明开发该类模块的作者,多个作者之间使用,分割

@version

????????标明该类模块的版本

@see

????????参考转向,也就是相关主题

@since

????????从哪个版本开始增加的

@param

????????对方法中某参数的说明,如果没有参数就不能写

@return

????????对方法返回值的说明,如果方法的返回值类型是void就不能写

@exception

????????对方法可能抛出的异常进行说明 ,如果方法没有用throws显式抛出的异常就不能写

package com.suyv.annotation;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-09 09:37
*@Description: TODO
*/
public class JavadocTest {
    /**
     * 程序的主方法,程序的入口
     * @param args String[] 命令行参数
     */
    public static void main(String[] args) {
    }

    /**
     * 求圆面积的方法
     * @param radius double 半径值
     * @return double 圆的面积
     */
    public static double getArea(double radius){
        return Math.PI * radius * radius;
    }
}

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

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

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

@SuppressWarnings: 抑制编译器警告

package com.suyv.annotation;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-09 09:40
*@Description: TODO
*/
public class AnnotationTest{

    public static void main(String[] args) {
        @SuppressWarnings("unused")
        int a = 10;
    }
    @Deprecated
    public void print(){
        System.out.println("过时的方法");
    }

    @Override
    public String toString() {
        return "重写的toString方法()";
    }
}

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

  • Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署。
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) { }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        doGet(request, response);
    }  
}
<servlet>
	<servlet-name>LoginServlet</servlet-name>
  <servlet-class>com.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>LoginServlet</servlet-name>
  <url-pattern>/login</url-pattern>
</servlet-mapping>
  • Spring框架中关于“事务”的管理
@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,readOnly=false,timeout=3)
public void buyBook(String username, String isbn) {
    //1.查询书的单价
    int price = bookShopDao.findBookPriceByIsbn(isbn);
    //2. 更新库存
    bookShopDao.updateBookStock(isbn);  
    //3. 更新用户的余额
    bookShopDao.updateUserAccount(username, price);
}
<!-- 配置事务属性 -->
<tx:advice transaction-manager="dataSourceTransactionManager" id="txAdvice">
  <tx:attributes>
    <!-- 配置每个方法使用的事务属性 -->
    <tx:method name="buyBook" propagation="REQUIRES_NEW" 
      isolation="READ_COMMITTED"  read-only="false"  timeout="3" />
  </tx:attributes>
</tx:advice>

4.3 三个最基本的注解

4.3.1 @Override
  • 用于检测被标记的方法为有效的重写方法,如果不是,则报编译错误!
  • 只能标记在方法上。
  • 它会被编译器程序读取。
4.3.2 @Deprecated
  • 用于表示被标记的数据已经过时,不推荐使用。
  • 可以用于修饰 属性、方法、构造、类、包、局部变量、参数。
  • 它会被编译器程序读取。
4.3.3 @SuppressWarnings
  • 抑制编译警告。当我们不希望看到警告信息的时候,可以使用 SuppressWarnings 注解来抑制警告信息
  • 可以用于修饰类、属性、方法、构造、局部变量、参数
  • 它会被编译器程序读取。
  • 可以指定的警告类型有(了解)
    • all,抑制所有警告
    • unchecked,抑制与未检查的作业相关的警告
    • unused,抑制与未用的程式码及停用的程式码相关的警告
    • deprecation,抑制与淘汰的相关警告
    • nls,抑制与非 nls 字串文字相关的警告
    • null,抑制与空值分析相关的警告
    • rawtypes,抑制与使用 raw 类型相关的警告
    • static-access,抑制与静态存取不正确相关的警告
    • static-method,抑制与可能宣告为 static 的方法相关的警告
    • super,抑制与置换方法相关但不含 super 呼叫的警告
    • ...

示例代码:

package com.suyv.annotation;

import java.util.ArrayList;

/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-09 09:42
*@Description: TODO
*/
public class TestAnnotation {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        int i;

        ArrayList list = new ArrayList();
        list.add("hello");
        list.add(123);
        list.add("world");

        Father f = new Son();
        f.show();
        f.methodOl();
    }
}

class Father{
    @Deprecated
    void show() {
        System.out.println("Father.show");
    }
    void methodOl() {
        System.out.println("Father Method");
    }
}

// 添加了@Override注解表明该方法属于重写方法,
// 不添加不会匹配父类的同名方法,相当于子类独有的方法
class Son extends Father{
    /*@Override
    void method01() {
        System.out.println("Son Method");
    }*/
}

4.4 元注解

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.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
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 {
}

4.5 自定义注解的使用

一个完整的注解应该包含三个部分:(1)声明(2)使用(3)读取

4.5.1 声明自定义注解
【元注解】
【修饰符】 @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();
}
import java.lang.annotation.*;

@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String columnName();
    String columnType();
}
4.5.2 使用自定义注解
@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 + '\'' +
        '}';
    }
}
4.5.3 读取和处理自定义注解

自定义注解必须配上注解的信息处理流程才有意义。

我们自己定义的注解,只能使用反射的代码读取。所以自定义注解的声明周期必须是RetentionPolicy.RUNTIME。

补充:理解main方法的语法

由于JVM需要调用类的main()方法,所以该方法的访问权限必须是public,又因为JVM在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。

又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。

命令行参数用法举例

public class CommandPara {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "] = " + args[i]);
        }
    }
}
//运行程序CommandPara.java
java CommandPara "Tom" "Jerry" "Shkstart"
//输出结果
args[0] = Tom
args[1] = Jerry
args[2] = Shkstart

文章来源:https://blog.csdn.net/weixin_46612641/article/details/134912273
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。