Java 学习 - Day 11

接口

定义

在Java 中,接口是一种特殊的抽象类型,它定义了一组方法签名但是不提供具体的实现。接口的主要作用是定义行为标准,即一组方法的规范,这些方法可以在不同类中具有不同实现。其特点如下:

        1、抽象方法:接口中的方法默认被public abstract修饰(公开且抽象),这意味着它只有方法签名而没有方法体

        2、常量:接口中可以包含常量,这些常量默认是被public static final 修饰的

        3、多重继承:Java 中只支持单继承,但接口提供了一种多重继承的机制,即一个类可以实现多个接口

        4、实现:任何实现类接口的非抽象类都必须提供接口方法的具体实现

        5、访问修饰符:接口中的方法只能是public ,不能使用private 或protected 修饰

        6、jdk 8之后接口中可以存在具有方法体的默认方法(default)或者静态方法(static),jdk 9之后可以存在被private 修饰的私有方法

        7、接口和抽象类一样不能被实例化,只能被实现

        8、如果一个类同时具有实现和继承关系,当其父类和实现的接口中具有相同成员时,可以通过super 和接口名来区分访问不同的成员


// 定义一个接口
public interface Transformer {
    
    // 等同于public static final int age = 1;
    int age = 1;

    // 等同于public abstract void becomeRobot();
    void becomeRobot();
    void becomeCar();
}

// 实现接口的类
public class Bumblebee implements Transformer {
    @Override
    public void becomeRobot() {
        System.out.println("Bumblebee transforms into a robot.");
    }

    @Override
    public void becomeCar() {
        System.out.println("Bumblebee transforms into a car.");
    }
}

// 使用实现接口的类
public class Main {
    public static void main(String[] args) {
        Bumblebee bumblebee = new Bumblebee();
        // 接口中的属性可以直接通过接口名进行访问,相当于特殊类
        System.out.println(Transformer.age);
        bumblebee.becomeRobot();
        bumblebee.becomeCar();
    }
}

适用场景

接口的应用场景:

        1、定义标准行为:接口定义一组行为规范,使得多个类可以通过实现同一个接口来提供一致的行为

        2、实现多态:通过接口,可以在运行时决定对象的实际类型(实现类)

        3、提供扩展性:接口为系统提供了一个扩展点,允许在不修改现有代码的情况下添加新的功能

        4、实现解耦:不同组件之间可以借助接口实现通信,使得各组件之间不需要直接依赖于对方的具体实现,这有助于降低系统的耦合度,提高可维护性和可测试性

        5、模拟多重继承:一个子类可以同时实现多个接口,以此来模拟多继承的效果

        6、框架和库的设计:许多框架和库中使用接口来定义其核心功能

        7、服务化和远程过程调用(RPC):接口可以定义服务端提供的服务,客户端通过实现相同的接口来调用服务端的行为

        8、插件化系统:接口可以用于定义插件化系统中的插件接口,可以通过实现插件接口实现系统的扩展

继承和接口

继承更适合用于表示is - a 的关系,例如Dog 是Animal 的一种,在这种情况下子类会继承父类的大部分属性和行为

接口则适合表示like - a 的关系,例如一个类可以实现eat 接口,表示它可以进行吃的行为,但具体如何去吃的细节因类而异

总的来说,继承强调的是类之间的相似性和层次关系,而接口强调的是行为的标准化和实现的多样性

接口中的多态

接口的多态特性:

        1、多态参数:与继承类似,允许方法接收指定参数类型及其实现类类型

        2、多态数组:与继承类似,数组中允许存放不同的实现类对象以及允许接口引用指向是西安类对象

        3、多态传递:接口之间允许继承,如果A接口继承了B接口,那么A接口的实现类可以看作是A或者B

类的完整成员

类的五大成员:

        属性 + 方法 + 构造器 + 代码块 + 内部类

内部类

定义

如果一个类嵌套于另一个类,那么就称嵌套类为内部类(inner class),被嵌套类称为外部类(outer class)。内部类的最大特点是可以访问外部类的私有属性,并且可以体现类与类之间的包含关系

局部内部类

           局部内部类是在一个方法体或一个作用域块中定义的类。这类内部类只能在其定义的方法或块中访问,并且它可以访问外部类的所有成员,包括私有成员。如果局部内部类需要访问方法中的局部变量,则这些变量必须被声明为final(或实际上不可更改,如对于基本类型来说,意味着它们一旦赋值就不能再改变;对于引用类型来说,意味着引用本身不能改变,但引用的对象内容可以改变),因为局部内部类不能修改它所在方法中的非 final 局部变量

                


public class OuterClass {
    private int localVar = 10;

    public void method() {
        // 局部变量必须是final或者实际上不可更改
        final int localVar = 20;

        // 定义一个局部内部类
        class LocalInnerClass {
            public void display() {
                // 可以通过外部类名.this 的方式来指定访问外部类成员
                System.out.println("Accessing Outer Class Field: " + OuterClass.this.localVar);
                System.out.println("Accessing Local Variable: " + localVar);
            }
        }

        // 创建局部内部类的实例
        LocalInnerClass localInner = new LocalInnerClass();
        localInner.display();
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        
        // Accessing Outer Class Field: 10
        // Accessing Local Variable: 20
        outer.method();
    }
}

匿名内部类

        匿名内部类是一种没有显式名称的内部类。它通常用于实现接口或继承类,并立即创建其实例,常用于简化代码,特别是在需要一次性使用的场合


public class Test {
    public static void main(String[] args) {
        // 不需要单独创建实现类,仅作一次性使用
        new IA() {
            @Override
            public void eat() {
                System.out.println("张三正在吃东西..");
            }
        }.eat();

        new IA() {
            @Override
            public void eat() {
                System.out.println("李四正在吃东西..");
            }
        }.eat();
    }
}

interface IA {
    void eat();
}

        在使用匿名内部类过程中,编译类型就是接口类型,而运行时类型则是匿名内部类。其名称的分配规则为 外部类名 + $ + 数字。

        匿名内部类在定义的时候同时具有定义类的特征和创建对象的特征(既是类的定义本身也是对象)

        匿名内部类的最佳实践就是当作实参来传递


public class Test {
    public static void main(String[] args) {
        test(new IA() {
            @Override
            public void eat() {
                System.out.println("匿名内部类中重写的eat 方法");
            }
        });
    }

    public static void test(IA ia) {
        ia.eat();
    }
}

interface IA {
    void eat();
}

成员内部类

        定义在一个外部类的内部,并且与外部类的其他成员(如方法、字段)处于同一级别,可以使用任意访问修饰符


public class Test {
    private String name;
    private int age;

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

    public void test() {
        new Inner().show();
    }

    public static void main(String[] args) {
        new Test().test();
    }
}

静态内部类

        与成员内部类相比,多了static 修饰,并且与类的其他静态成员处于同一级别,可以使用任意访问修饰符


public class Test {
    private String name;
    private int age;

    static class Inner {
        public void show() {
            System.out.println("Inner class..");
        }
    }
    
    public static void main(String[] args) {
        // 可以直接创建内部类对象来访问其成员
        new Inner().show();
    }
}

如果是外部类要创建静态内部类对象,其格式如下:new OutClass.InnClass()

枚举

定义

        一种特定的数据类型,它由一组固定的命名常量组成。这些常量通常代表一组有限的、互不相同的值。枚举可以用来表示一系列相关的符号常量,比如一周中的每一天、颜色、方向等。简单来说可以把枚举看作一种特殊的类,里面只包含一组有限的特定的对象

        与普通类相比:

                1、枚举在定义时不需要提供set 方法,因为其中的对象通常是只读的

                2、对枚举对象或属性使用final + static 修饰,实现底层优化(使用属性时不会导致整个类的加载)

                3、枚举对象名与常量名称的命名规则相同,即都使用大写

                4、枚举对象根据需求也可以有多个属性

普通类实现: 


public class Test {
    public static void main(String[] args) {
        // season: 秋天
        System.out.println("season: " + Season.AUTUMN.getName());
    }
}

class Season {
    private String name;

    public String getName() {
        return name;
    }

    public Season(String name) {
        this.name = name;
    }

    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("冬天");
}

枚举类实现:


public class Test {
    public static void main(String[] args) {
        // season: 秋天
        System.out.println("season: " + Season.AUTUMN.getName());
    }
}

enum Season {
    // 枚举常量定义在最前面可以保证可读性和便于扩展
    // 枚举实例是在编译时由编译器自动创建的,而不是显示调用构造器创建
    SPRING("春天"), SUMMER("夏天"), 
    AUTUMN("秋天"), WINTER("冬天");

    private String name;

    public String getName() {
        return name;
    }

    // 构造器私有化是为了确保枚举类型的完整性,避免客户端通过构造器创建枚举实例
    private Season(String name) {
        this.name = name;
    }
}

        当使用enum 定义一个枚举类的时候,默认为该枚举类继承Enum 类(所以被enum 修饰的类无法继承其他类,但是可以实现接口)。并且常量后跟的参数列表是为了匹配构造器,如果使用默认构造器则可以省略括号

        多次从枚举类中获取同一个枚举对象的时候是同一个对象,是单例模式的一种实现


public class Test {
    public static void main(String[] args) {
        Season season1 = Season.AUTUMN;
        Season season2 = Season.AUTUMN;
        // true
        System.out.println(season1 == season2);
    }
}

enum Season {
    SPRING("春天"), SUMMER("夏天"),
    AUTUMN("秋天"), WINTER("冬天");

    private String name;

    public String getName() {
        return name;
    }
    
    private Season(String name) {
        this.name = name;
    }
}

方法

        1、toString:返回当前的对象名,子类可以对其进行重写以返回对象属性信息

        2、name:返回对象名,子类不能重写(优先使用toString)

        3、ordinal:返回当前对象在枚举中的位置,默认从0 开始

        4、values:返回当前枚举中所有的常量(一个数组)

        5、valueOf:将字符串对象转为枚举对象,要求字符串必须为已有的常量名

        6、compareTo:比较两个枚举对象,默认为在枚举当中的位置

注解

定义

在Java中,注解是一种元数据(Metadata),用于为代码提供额外的信息或说明。注解不会直接影响程序的逻辑,但可以被编译器或者运行时环境用来进行一些特定的操作,如验证参数正确性、生成文档、执行某些编译时或运行时的处理等

使用@interface关键字来定义一个注解。注解可以包含零个或多个成员(也被称为元素),这些成员类似于接口中的抽象方法。成员的返回类型可以是基本类型(如int、boolean、char、byte、short、long、float、double)、String、Class、Enum、其他注解类型,或者是上述类型的数组

定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,这里表示运行时可见
@Target(ElementType.METHOD) // 注解的目标应用位置,这里表示可以应用在方法上
public @interface MyAnnotation {
    String value(); // 必须提供的成员
    String description() default "Default Description"; // 可选成员,提供默认值
}

使用注解

public class MyClass {
    @MyAnnotation(value="Hello", description="Greeting Message")
    public void greet() {
        System.out.println("Hello, world!");
    }
}

处理注解

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) {
        Method greetMethod = MyClass.class.getMethod("greet");
        MyAnnotation myAnnotation = greetMethod.getAnnotation(MyAnnotation.class);
        if (myAnnotation != null) {
            System.out.println("Value: " + myAnnotation.value());
            System.out.println("Description: " + myAnnotation.description());
        }
    }
}

通过使用元注解,可以实现自定义注解的行为,使其更加符合特定的业务需求

基本注解

        1、@Override:用于标记子类中的方法,以表明该方法旨在重写其超类中的方法。如果方法没有正确地重写超类中的方法,编译器将生成错误。主要目的是为了增强代码的安全性和可维护性,确保方法重写的意图得以实现,并且使代码更易于理解和维护

public class BaseClass {
    public void display() {
        System.out.println("Base Class Display");
    }
}

public class SubClass extends BaseClass {
    @Override
    public void display() {
        System.out.println("Sub Class Display");
    }
}

        2、@Deprecated:用于标记那些不再推荐使用的方法或类。当编译器检测到使用了@Deprecated注解的元素时,会发出警告

public class OldClass {
    @Deprecated
    public void oldMethod() {
        System.out.println("This is an old method.");
    }

    public void newMethod() {
        System.out.println("This is a new method.");
    }
}

        3、@SupressWarnings:用于抑制编译器产生的特定警告信息。这对于某些已知但不需要修正的情况特别有用,以让编译器显示更加重要的信息

public class Example {
    @SuppressWarnings("unchecked")
    public void someMethod() {
        List list = new ArrayList(); // 无类型参数的列表赋值给 List 引用
        list.add("Hello");
        String str = (String) list.get(0); // 安全因为只添加了字符串
    }
}

元注解

        用来注解其他注解的一种特殊类型的注解。它们提供了对注解类型的描述信息,例如注解的生命周期、应用范围等。Java中有几个标准的元注解,分别是@Target、@Retention、@Documented和@Inherited

@Target元注解用于指定一个注解可以应用到哪些程序元素上。它接受一个ElementType枚举类型的数组作为参数,表示注解可以应用于的目标元素类型,如类、方法、字段等

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) // 只能应用在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时有效
public @interface MyMethodAnnotation {
    String value();
}

@Retention元注解用于指定注解的生命周期,即注解在哪个阶段仍然可用。它接受一个RetentionPolicy枚举类型的单个值作为参数,表示注解在编译后的保留策略

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) // 应用在类或接口上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时有效
public @interface MyRuntimeAnnotation {
    String value();
}

@Documented元注解用于指定被注解的元素是否应该包含在生成的文档中。如果一个注解被@Documented注解,那么它的信息会被包含在API文档中

import java.lang.annotation.ElementType;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) // 应用在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时有效
@Documented // 包含在文档中
public @interface MyDocumentedAnnotation {
    String value();
}

@Inherited元注解用于指定被注解的类的子类是否也应该继承该注解。如果一个注解被@Inherited注解,那么该注解可以被子类继承,并且子类也被标注了相同的注解。注意,@Inherited只适用于类级别的注解,对方法、字段等其他元素的继承不起作用

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) // 应用在类或接口上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时有效
@Inherited // 可以被子类继承
public @interface MyInheritedAnnotation {
    String value();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值