Java抽象类、内部类、包装类、接口、Lambda表达式

Java中的抽象类(Abstract Class)是一种特殊类型的类,它无法被实例化,只能被用作其他类的基础。抽象类用于定义具有共同特征和行为的一组相关类的共同结构和方法。抽象类可以包含抽象方法(没有具体实现的方法)和具体方法(有实际实现的方法)。

文章目录

一、抽象类_abstract,不可被实例化:

作为他类的基类,用于定义共同的特征和行为,抽象类中的抽象方法未明确具体的方法
尽量将字段和方法(不论是否为抽象类)放在超类(不论是否是abstract)中

父类具有的是一般性,也可能抽象。
但从某种角度来说,其作为派生其他类的基类,而不是用到自己的想要的方式的特点实例。

如:

		Person
	       |
  ——————————————————————————————
  |								|
  Employee				Student

格式:

[修饰符]abstract class ClassName [extends 父类] [implements 接口列表]{
	成员内容}

但是可以创建抽象类的对象变量(object variable),但是这样一个变量只能引用子类的对象。

Person p =new Student();

p是抽象类型Person的一个变量,引用了非抽象子类Student的一个实例。

在这里插入图片描述
其中三角形和长方形的类中的实现了面积的方法是具体的,不再是抽象类了

好处:类似与方法重载,能够使用其特定的方法

抽象类可以用作一个通用的基类,它定义了一组通用的属性和方法。然后,具体的子类可以继承这个抽象类,并根据具体的需求来实现或扩展其中的方法。这种结构允许你在处理多个不同的子类时,使用抽象类类型的引用,从而获得多态性和灵活性的好处。
在这里插入图片描述

1.1 抽象方法_abstract,不可被实例化 ,子类必须实现

==其相当于子类实现本体的具体的方法的**占位符**==。
[修饰符]abstract 返回类型 方法名 ([参数列表][throws 异常列表]

抽象方法没有方法体,直接 分号;结束
含有抽象方法的类必须是抽象类,不能被实例化
抽象方法必须在子类中实现

如果抽象类中没有抽象方法,在子类在定义方法。就不能用父类引用子类中新增的内容了(和多态中类似,方法表

扩展抽象类:
(1)保留抽象类的部分或者所有抽象方法仍未定义,这样子类也必须标记为抽象类;

// 抽象类
abstract class Animal {
    abstract void makeSound();  // 抽象方法
}

// 抽象子类,因为没有实现抽象方法
abstract class Bird extends Animal {
    // 这里没有提供对抽象方法 makeSound 的实现
}

// 具体子类,实现了抽象方法,可以被实例化
class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Dog barks");
    }
}

// 具体子类,实现了抽象方法,可以被实例化
class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Cat meows");
    }
}

Bird 是一个抽象子类,因为它继承了抽象父类 Animal,但没有提供对抽象方法 makeSound 的实现。而 Dog 和 Cat 都是具体子类,它们实现了抽象方法 makeSound,因此可以被实例化。

(2)定义全部方法,子类就不会抽象了。

// 抽象类
abstract class Shape {
    abstract double calculateArea();  // 抽象方法

    double calculatePerimeter() {
        return 0;  // 这里只是示范,具体计算逻辑需要实现类提供
    }
}

// 具体子类,实现了抽象方法,可以被实例化
class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double calculateArea() {
        return Math.PI * radius * radius;
    }

    @Override
    double calculatePerimeter() {
        return 2 * Math.PI * radius;
    }
}

// 具体子类,实现了抽象方法,可以被实例化
class Rectangle extends Shape {
    double width;
    double height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    double calculateArea() {
        return width * height;
    }

    @Override
    double calculatePerimeter() {
        return 2 * (width + height);
    }
}

在这里插入图片描述

1.2 抽象类的主要特点包括:

强制子类必须按照特定的格式进行方法重写
抽象方法所在的类就是抽象类
  1. 不能被实例化:不能使用new关键字直接创建抽象类的实例。

  2. 可以包含抽象方法:抽象类可以定义抽象方法,这些方法没有实际的代码实现,只有方法签名。子类必须实现抽象类中的所有抽象方法。

     如果一个类包含至少一个抽象方法,那么这个类必须声明为抽象类。
    
  3. 可以包含具体方法:抽象类也可以包含具体的方法实现,这些方法在抽象类中有默认的实现,子类可以选择性地覆盖这些方法。

  4. 用于继承:其他类可以继承抽象类,并通过实现抽象方法来提供具体的实现。子类继承了抽象类的属性和方法。

  5. 用于多态:抽象类可以用于实现多态性,通过父类引用指向子类对象。

1.3 可以有构造方法: 这些构造方法在子类实例化时可以被调用【给子类对象赋值】,通常用于初始化抽象类中的字段。

虽然抽象类本身不能被实例化,但是抽象类的构造方法仍然有其用途。抽象类的构造方法在以下几种情况下是有用的:

  1. 初始化抽象类的字段: 抽象类可以包含字段(成员变量),这些字段可以在构造方法中进行初始化。子类在实例化时会调用父类的构造方法来初始化父类的字段。

  2. 在子类构造方法中调用: 当子类实例化时,它的构造方法会隐式或显式地调用父类的构造方法,以确保父类的初始化逻辑得以执行。

  3. 构造方法的继承: 子类继承了父类的构造方法,尽管不能直接实例化抽象类,但子类在实例化时仍然需要调用适当的构造方法,以初始化继承自父类的字段。

  4. 调用抽象类中的方法: 构造方法也可以用于调用抽象类中的其他方法,无论这些方法是抽象的还是具体的。这些方法可能在子类的构造方法中执行特定的初始化逻辑。

尽管抽象类本身不能被实例化,但构造方法在创建其子类的对象时扮演着关键的角色。子类的构造方法在执行时会先调用父类的构造方法,以确保继承链中的初始化步骤得以完成。这有助于保持继承关系中的数据完整性和一致性。

1.4 抽象类的子类要么重写抽象类中的所有抽象方法要么是抽象类:

如果一个类继承了一个抽象类,并且不是抽象类本身,那么它要么必须提供实现来覆盖抽象类中的所有抽象方法,要么就必须自己声明为抽象类。如果子类没有提供实现,那么它也必须声明为抽象类。

以下是一个简单的Java抽象类的示例:

abstract class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    abstract void makeSound(); // 抽象方法,子类必须实现

    void eat() {
        System.out.println(name + " is eating."); // 具体方法
    }
}

class Dog extends Animal {
    Dog(String name) {
        super(name);
    }

    void makeSound() {
        System.out.println(name + " barks.");
    }
}

class Cat extends Animal {
    Cat(String name) {
        super(name);
    }

    void makeSound() {
        System.out.println(name + " meows.");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("Buddy");
        Cat cat = new Cat("Whiskers");

        dog.makeSound();
        cat.makeSound();

        dog.eat();
        cat.eat();
    }
}

在这个示例中,Animal是一个抽象类,它定义了一个抽象方法makeSound()和一个具体方法eat()DogCatAnimal的子类,它们必须实现makeSound()方法。在Main类中,通过多态性调用不同子类的方法。

抽象类的子类在继承抽象类时,通常必须实现抽象类中的所有抽象方法。抽象类是一种不能被实例化的类,它可以包含普通的方法和属性,也可以包含抽象方法。抽象方法是在抽象类中声明但没有提供具体实现的方法,它相当于一种约定,要求子类必须提供具体的实现。
······
当一个类继承一个抽象类时,如果该子类本身不是抽象类,那么它必须提供实现抽象类中所有抽象方法的具体实现。如果子类没有实现抽象类中的全部抽象方法,那么这个子类也必须被声明为抽象类。
·······
在面向对象编程中,抽象类和抽象方法用于定义一个通用的接口或者约定,以便子类可以继承并提供具体的实现。这种机制能够强制子类按照预期的方式来实现方法,从而保证代码的一致性和可维护性。

详细地讨论一下。

  1. 抽象方法(Abstract Methods): 抽象方法是在抽象类中声明的方法,它没有具体的实现代码,只有方法签名。子类必须实现抽象类中的抽象方法。这适用于在不同的子类中有不同的实现,而且你希望强制子类提供自己的实现。抽象方法通常用于定义基本操作,但这些操作在不同的子类中可能有不同的行为。

  2. 成员方法(Concrete Methods): 成员方法是在抽象类中有具体实现的方法。这些方法可以在抽象类中提供通用的、共享的实现,以避免在每个子类中重复编写相同的代码。子类可以选择性地覆盖这些方法,以适应特定的需求。成员方法通常用于实现通用的功能,这些功能在多个子类中都可以共享。

总之,你可以这样理解:

  • 使用抽象方法:当你希望在不同的子类中有不同的实现,而且这些实现在子类中是必需的时,你应该使用抽象方法。抽象方法强制子类提供自己的实现,确保基类的方法在子类中被正确覆盖。

  • 使用成员方法:当你有一些通用的、可复用的代码实现,可以在抽象类中提供默认的具体实现。子类可以直接继承这些方法,避免重复编写相同的代码。子类也可以选择性地覆盖这些方法,以适应特定需求。

需要注意的是,抽象类可以同时包含抽象方法和成员方法,这取决于你的设计需求。通过合理地使用抽象方法和成员方法,可以在代码中实现良好的组织和重用。

举例子;

package oop_useAbstractClass;

// 定义一个抽象类 Shape,用于表示各种几何图形
public abstract class Shape {
    public int width; // 用于存储几何图形的宽度
    public int height; // 用于存储几何图形的高度

    // 构造方法,用于初始化宽度和高度
    public Shape(int width, int height) {
        this.width = width;
        this.height = height;
    }

    // 声明一个抽象方法 area(),用于计算图形的面积
    public abstract double calculateArea();
}

package oop_useAbstractClass;

// 长方形类,继承自抽象类 Shape
public class Rectangle extends Shape {
    // 构造函数,接受长方形的长度和宽度作为参数
    public Rectangle(int length, int width) {
        // 调用父类的构造函数来初始化长度和宽度
        super(length, width);
    }

    // 重写父类中的抽象方法,计算长方形的面积
    @Override
    public double calculateArea() {
        // 长方形的面积计算公式:长度 * 宽度
        return width * height;
    }
}

package oop_useAbstractClass;

public class Triangle extends Shape {
    public Triangle(int width, int height) {
        super(width, height);
    }

    // 重写父类中的抽象方法,计算三角形面积的功能
    @Override
    public double calculateArea() {
        return 0.5 * width * height;
    }
}

package oop_useAbstractClass;


public class ShapeTest {
    public static void main(String[] args) {
        // 创建一个长方形对象并初始化其长和宽
        Rectangle rectangle = new Rectangle(5, 4);
        // 计算并输出长方形的面积
        System.out.println("长方形的面积为:" + rectangle.calculateArea());

        // 创建一个三角形对象并初始化其底边和高
        Triangle triangle = new Triangle(2, 5);
        // 计算并输出三角形的面积
        System.out.println("三角形的面积为:" + triangle.calculateArea());
    }
}

在这里插入图片描述

1.5 抽象类和接口的设计目的区别

抽象类主要用于表示一种类的模板,它关注的是事物的本质特征和行为
接口则更侧重于定义对象之间的通信协议,它关注的是对象能做什么,而不是对象是什么。

1.5.1 抽象类

  • 目的:抽象类是为了表示一种通用的抽象概念,其中可能包含一些具体的方法实现,也可以包含抽象方法(即声明但不实现具体代码的方法)。抽象类通常用于定义子类的通用行为和结构,并且可以有部分方法是具体实现的。
  • 特点:可以包含成员变量、构造方法、具体方法和抽象方法。子类必须实现抽象方法,但可以选择性地重写具体方法。

1.5.2 接口

  • 目的:接口是为了定义一种规范或契约,它只包含抽象方法的声明和常量的定义,不包含任何具体实现。接口定义了一组行为或功能,实现接口的类必须提供接口中定义的所有方法的具体实现。
  • 特点:只能包含常量和抽象方法的定义,不能包含成员变量或具体方法。类可以实现多个接口,从而实现多重继承的效果。

1.5.3 总结

  • 抽象类适合用于具有部分默认实现的类层次结构,可以提供一些公共的方法实现。
  • 接口适合定义多个类共同遵循的行为规范,不关心实现的细节,强调了行为的一致性和规范性。

二、内部类

内部类可以访问其外部类的所有成员,包括私有数据和方法,这使得内部类可以与其外部类有更紧密的联系。此外,内部类也可以隐藏在其外部类中,这有助于隐藏实现细节并提高代码的封装性。

2.1 成员内部类:写在成员位置的,属于外部类成员

public class OuterClass {
    class InnerClass {
        // ...
    }
}

获取成员内部类对象的两种方式:

        方式一:外部类编写方法,对外提供内部类对象(private)

        方式二:直接创建
        格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
        范例:Outer.Inner oi = new Outer().new Inner();

举例

public class New {
    private final int a = 10;

   class Inner {
        private final int a = 20;

        public void show() {
            int a = 30;
            //就近原则
            System.out.println(a);
            //内部类的this
            System.out.println(this.a);
            //New中隐藏的this的值,这个this指向的是New。this,是外部的a的地址
            System.out.println(New.this.a);
        }
    }
}  
import java.util.Scanner;

public class NewClass {
    public static void main(String[] args) {
/*                 01这种方式是在创建内部类实例的同时创建了外部类的匿名实例。
        这种方式的缺点是你无法再次访问到这个外部类实例,因为你没有保留对它的引用。*/
        New.Inner oi = new New().new Inner();
        oi.show();

/*        02这种方式是先创建外部类的实例,然后使用这个实例来创建内部类的实例。
        这种方式的优点是你保留了对外部类实例的引用,可以在后续的代码中再次使用这个外部类实例。*/
        New outer = new New();  // 使用 new 关键字创建 New 类的实例
        New.Inner inner = outer.new Inner();  // 使用外部类的实例创建内部类的实例
        inner.show();
        Scanner sc = new Scanner(System.in);
        sc.next();


    }
}

2.2 静态内部类

这是一种最简单的内部类,它是一个完全独立的类,只是为了包装方便和隐藏而定义在另一个类内部。它不能访问外部类的非静态成员。

    //1.静态内部类也是成员内部类的一种
    //2.静态内部类只能访问外部类中静态方法和静态变量
    //          如果想要访问非静态需要创建对象
public class OuterClass {
    static class StaticNestedClass {
        // ...
    }
}
创建静态内部类对象的格式:
	外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用静态方法的格式:
    外部类名.内部类名.方法名();
只要是静态的东西,都可以用类名点直接获取
public class Outer {

    int a = 10;
    static int b = 20;

    static class Inner {
        public void show1(){
            System.out.println("非静态的方法被调用了"+Outer.b);
        }

        public static void show2(){
            System.out.println("静态的方法被调用了"+Outer.b);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer.Inner();
        oi.show1();
        Outer.Inner.show2();

    }
}

在这里插入图片描述

2.3 局部内部类

这种内部类是定义在一个方法或者一个作用域里面的类,它的生命周期和作用域是一致的,离开作用域,它就不能被访问。

public class OuterClass {
    void someMethod() {
        class LocalInnerClass {
            // ...
        }
    }
}
  1. 定义在方法中:局部内部类是在一个外部类的方法里定义的类。

  2. 作用域限制:它只能在定义它的方法内部被实例化和使用,外部无法访问。

  3. 访问权限:它可以无限制地访问外部类的成员。对于方法内的局部变量,它可以访问被声明为final的变量(在Java 8及以后版本中,局部变量不必显式声明为final,但必须事实上是final,即不被后续修改)。

public class OuterClass {

    public void display() {
        final int localVar = 50; // 在Java 8及以后,不必显式声明为final,但必须不被修改

        // 局部内部类定义在方法内部
        class LocalInnerClass {
            public void print() {
                // 可以访问外部类的成员
                System.out.println("外部类的成员变量值");
                // 也可以访问方法内的局部变量(必须是final或事实上的final)
                System.out.println("方法内的局部变量值: " + localVar);
            }
        }

        // 在方法内部创建局部内部类的实例并使用
        LocalInnerClass lic = new LocalInnerClass();
        lic.print();
    }
}

public class Main {
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        outer.display(); // 调用含有局部内部类的方法
    }
}

案例2


public class Other {
    int b=20;
    public void show() {

        int a = 10;//pro 私有,pub,默认只能修饰成员,局部变量不可修饰
        //局部内部类
        class Inner {//同理不可
            String name;
            int age;

            public void method1() {
                //外部类的b可以直接获取
                System.out.println(a + " " + b);
                System.out.println("non-static-method");
            }

            public static void method2() {
                System.out.println("static -- method");
            }
        }

            //创建局部内部类的对象
            //外界是无法直接使用到的,需要在方法内部创建对象并使用
            Inner i =new Inner();
            System.out.println(i.name+" "+i.age);
            i.method1();
            Inner.method2();
        }

    }


import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        //无法直接 使用show方法里面的局部变量a
        Other o = new Other();
        o.show();
        Scanner sc =new Scanner(System.in);
        sc.next();

    }

}

2.4 匿名内部类:本质上隐藏了名字的内部类:继承、方法重写,重写对象

这种内部类没有类名,一般用于只需要使用一次的场景。
//格式:
    //new 类名或者接口名(){
    //    重写方法
    //};
    //
//举例:
    //new Inter(){
    //    public void show(){
    //}

在这个例子中,HelloWorld 是一个接口,我们在 sayHello 方法中创建了一个匿名内部类来实现这个接口,并重写了 greet 方法。然后我们创建了这个匿名内部类的一个实例 englishGreeting 并调用了 greet 方法。

interface HelloWorld {
    void greet();
}

public class AnonymousClassExample {
    public void sayHello() {
        HelloWorld englishGreeting = new HelloWorld() {
            public void greet() {
                System.out.println("Hello!");
            }
        };

        englishGreeting.greet(); // 输出 "Hello!"
    }

    public static void main(String[] args) {
        AnonymousClassExample example = new AnonymousClassExample();
        example.sayHello();
    }
}

三、包装类

为了在基本数据类型和对象之间建立一个桥梁

Java的包装类(Wrapper Classes)用于在需要对象而基本数据类型不可用的情况下使用,例如在集合类中。

  • byte - Byte
  • short - Short
  • int - Integer
  • long - Long
  • float - Float
  • double - Double
  • char - Character
  • boolean - Boolean

GPT
在这里插入图片描述

    public static void main(String[] args) {
// 使用包装类来存储基本数据类型的值
        Integer intValue = 5;
// 使用包装类实现泛型
        List<Integer> integerList = new ArrayList<>();
        integerList.add(10);
// 包装类允许为null
        Integer nullableValue = null;
        integerList.add(nullableValue);
// 使用包装类的实用方法
        int intFromStr = Integer.parseInt("123");
        integerList.add(intFromStr);
        System.out.println(integerList);

// 包装类在集合中的应用
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        System.out.println(numbers);

    }

在这里插入图片描述

3.1 应用

Java的包装类(Wrapper Classes)是为了将基本数据类型(如int、float、char等)转换为对象。在Java中,基本数据类型是非对象的,它们不具备面向对象的特性,无法参与面向对象的操作,例如在集合类中存储对象或作为参数传递给方法等。

包装类的主要目的有以下几点:

  1. 将基本数据类型转换为对象: 包装类允许将基本数据类型转换为对应的对象,从而可以在需要对象的情况下使用基本数据类型。

  2. 在集合类中使用: 集合类(如ArrayList、LinkedList等)只能存储对象而不能存储基本数据类型。使用包装类可以将基本数据类型转换为对象,然后将其存储在集合中。

    ArrayList<Integer> list = new ArrayList<>();
    list.add(5); // 自动装箱,将int类型转换为Integer对象
    int value = list.get(0); // 自动拆箱,将Integer对象转换为int类型
    
  3. 作为泛型的实际类型参数: 泛型不能使用基本数据类型,只能使用对象类型。如果需要在泛型中使用基本数据类型,就需要使用对应的包装类。

    List<Double> doubleList = new ArrayList<>();
    doubleList.add(3.14); // 自动装箱,将double类型转换为Double对象
    
  4. 提供一些实用方法: 包装类提供了一些实用的方法,使得对基本数据类型的操作更加方便。例如,Integer类提供了parseInt()方法,用于将字符串转换为整数。

    String numStr = "123";
    int num = Integer.parseInt(numStr); // 将字符串转换为整数
    

总的来说,包装类的存在使得在面向对象的环境中能够更方便地处理基本数据类型,同时允许它们参与到需要对象的操作中。自动装箱和自动拆箱功能使得在基本数据类型和包装类之间的转换更加方便。

3.2 以Integer为例获取Integer的包装类对象

在Java中,获取一个包装类的对象有两种主要的方式:直接创建对象和使用静态方法。

1. 直接创建对象:

// 以Integer为例
Integer integerObject = new Integer(42); // 使用构造方法创建对象

2. 使用静态方法:

a. valueOf() 方法:
// 以Integer为例
Integer integerObject = Integer.valueOf(42); // 使用静态方法valueOf()
b. parseXxx() 方法:
// 以Integer为例,Xxx表示具体的基本数据类型,如int、double等
String str = "42";
Integer integerObject = Integer.parseInt(str); // 使用静态方法parseInt()

上述代码中,valueOf() 方法和 parseXxx() 方法(如 parseInt()parseDouble() 等)都是包装类提供的静态方法,用于将基本数据类型的值或字符串转换为包装类的对象。这些方法返回相应的包装类对象,使得操作基本数据类型更加灵活。

3.拆箱(Unboxing)和装箱(Boxing)

  1. 拆箱(Unboxing)

    • 定义: 将包装类型(例如,对象或类)中的值提取出来,转换为基本数据类型。
    • 示例: 在Java中,如果你有一个Integer对象,可以通过拆箱将其转换为int类型。
      Integer boxedValue = 42; // 装箱
      int unboxedValue = boxedValue; // 拆箱
      
  2. 装箱(Boxing)

    • 定义: 将基本数据类型的值包装在一个对象中,以便在需要对象的上下文中使用。
    • 示例: 在Java中,将一个int类型的值装箱到Integer对象中。
      int primitiveValue = 42;
      Integer boxedValue = primitiveValue; // 装箱
      
public class BoxingUnboxingExample {
    public static void main(String[] args) {
        // 装箱
        Integer boxedValue = 42;
        System.out.println("Boxed value: " + boxedValue);

        // 拆箱
        int unboxedValue = boxedValue;
        System.out.println("Unboxed value: " + unboxedValue);

        // 自动装箱和自动拆箱
        Integer autoBoxedValue = 55; // 自动装箱
        int autoUnboxedValue = autoBoxedValue; // 自动拆箱

        System.out.println("Auto-Boxed value: " + autoBoxedValue);
        System.out.println("Auto-Unboxed value: " + autoUnboxedValue);
    }
}

4.Integer缓存机制。这个机制确实存在,并且主要与自动装箱(autoboxing)有关。

在Java 5及更高版本中,Integer类引入了一个小的缓存来存储-128到127之间的整数值的单个实例。这是为了优化性能和内存使用。

当你使用自动装箱(即,将基本类型整数转换为Integer对象)时,如果该整数的值在这个范围内,Java会返回预先存在的Integer实例,而不是创建一个新的。

Integer i1 = 127;  
Integer i2 = 127;  
System.out.println(i1 == i2); // 输出 true,因为i2和i1引用同一个对象

但是,对于超出此范围的值或手动使用Integer构造函数创建的对象,此缓存机制不会适用。

Integer i3 = Integer.valueOf(128); // 创建一个新的Integer对象  
Integer i4 = Integer.valueOf(128); // 创建一个新的Integer对象  
System.out.println(i3 == i4); // 输出 false,因为i3和i4引用不同的对象

此缓存机制旨在提高性能并减少内存使用,尤其是在频繁创建和销毁小整数值的对象时。但是,它并不适用于所有情况,因此对于超出此范围的值或手动创建的对象,它不会起作用。

5. 运算

Integer i1 = new Integer(1);
        Integer i2 = new Integer(2);
        //    拆箱、相加、装箱
        int res=i1.intValue()+i2.intValue();
        Integer resBox=new Integer(res);
        System.out.println(resBox);
 String str2 = Integer.toBinaryString(100);
        System.out.println(str2);

        String str8 = Integer.toOctalString(100);
        System.out.println(str8);

        String str16 = Integer.toHexString(100);
        System.out.println(str16);
        //会以某进制的形式,运算会转化为10进制
        int num = Integer.parseInt("100");
        System.out.println(num+12);

        //8种包装类当中,除了Character都有对应的parseXxx的方法,
        // 进行类型转换

        boolean str0 = Boolean.parseBoolean("true1");
        boolean str1 = Boolean.parseBoolean("true");
        System.out.println(str0+" "+str1);

3.3 ArrayList

类 ArrayList<E> 泛型:限定集合中的存储数据的类型
打印对象不是地址值,而是集合中存储数据内容
在展示的时候会拿[]把所有的数据进行包裹

3.3.1 集合和数组之间的对比

  • 数组长度是固定的,一旦初始化了,长度就确定了,不能改变
  • 数组可以存储基本数据类型和引用数据类型,集合只能存储引用数据类型,若是基本数据类型需要转化为对应的包装类

3.3.2 增删改查的方法

// 增加元素
public boolean add(E e); //在集合末尾添加元素
public void add(int index, E element);  //在指定位置添加元素

// 删除元素
public E remove(int index);  //根据索引删除元素
public boolean remove(Object o);  //根据元素删除元素,返回被删除的元素,当删除了一个不存在的元素级会false

public void clear();  //清空集合

// 修改元素
public E set(int index, E element);

// 查找元素
public E get(int index);  //根据索引获取元素
public int size();  //获取集合中元素的个数
// 增加元素
ArrayList<String> list = new ArrayList<>();
list.add("Element");

// 删除元素
list.remove("Element");

// 修改元素
list.set(0, "NewElement");

// 查找元素
String element = list.get(0);

使用for循环遍历

for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

使用增强for循环遍历

for (String s : list) {
    System.out.println(s);
}
package Learn02ArrayList.What;

import java.util.ArrayList;

public class Demo01 {
    public static void main(String[] args) {

        //创建一个ArrayList对象,该对象可以存储字符串
        ArrayList<String> list = new ArrayList<>();
        System.out.println(list); //打印出:[ ]

        //ArrayList中添加元素
        list.add("Hello");
        list.add("World");
        System.out.println(list); //打印出:[Hello, World]
        list.add(0, "Java");
        System.out.println(list); //打印出:[Java, Hello, World]

        //ArrayList中删除元素
        boolean x = list.remove("Hello");
        System.out.println(x); //打印出:true
        System.out.println(list); //打印出:[Java, World]
        list.remove(0);
        System.out.println(list); //打印出:[World]
        list.clear();
        System.out.println(list); //打印出:[ ]

        //ArrayList中修改元素
        list.add("Hello");
        list.add("World");

        list.set(0, "Java");

        System.out.println(list); //打印出:[Java, World]

        //ArrayList中获取元素

        String s = list.get(0);

        System.out.println(s); //打印出:Java

        int index = list.indexOf("World");

        System.out.println(index); //打印出:1

        System.out.println(list.size()); //打印出:2

        System.out.println(list.isEmpty()); //打印出:false

        System.out.println(list.contains("Hello")); //打印出:false

        System.out.println(list.contains("Java")); //打印出:true

        System.out.println(list.toArray()); //打印出:[Ljava.lang.Object;@1b6d3586

    }
}

四、接口(interface)

在Java中,接口(interface)是一种抽象的数据类型,它定义了一组方法的规范,但没有提供具体的实现。接口可以看作是对类行为的一种约定,通过实现接口,类能够拥有相同的行为,并且能够与其他实现该接口的类进行交互。

在Java中,通过使用关键字 “interface” 来声明一个接口。接口中可以包含常量和抽象方法,默认情况下所有的方法都是公共、抽象且不可变的。类通过使用 “implements” 关键字来实现一个或多个接口,并需要提供这些接口中定义的所有方法的具体实现。

public class ClassName [extend 父类] implements 接口名1 接口2{}

接口的子类(实现类)

重写接口中的所有抽象方法(常用的)
抽象类

抽象类多用于父类
接口多用于行为,一种规则,对于行为的抽象

4.1 例子(接口+抽象类)

(1)接口+抽象类

public interface Swim {
	public abstract void swim();
}
public abstract class Animal  {
	private String name;
	private int age;
	public Animal() {
	}
	public Animal(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public abstract void eat(String a);

}

(2)子类(实现类)

public class Frog extends Animal implements Swim{
	public Frog() {
	}
	public Frog(String name, int age) {
		super(name, age);
	}
	@Override
	public void swim() {
		System.out.println("Frog swim");
	}
	@Override
	public void eat(String a) {
		System.out.println("Frog eat "+a);
	}
}

多来几个

public class Dog extends Animal implements Swim {
    public Dog() {
    }
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void swim() {
        System.out.println("DOg Swim");
    }
    @Override
    public void eat(String a) {
        System.out.println("DOG EAT");
    }
}

(3)测试类

public class Test {
	public static void main(String[] args) {
		 Animal frog = new Frog("Frog", 1);
		 System.out.println(frog.getName() + " " + frog.getAge());
		 frog.eat("s");
		 //frog.swim();

		Frog frog2 = new Frog("Frog2", 2);
		System.out.println(frog2.getName() + " " + frog2.getAge());
		frog2.eat("ss");
		frog2.swim();

		Dog dog = new Dog("d", 2);
		System.out.println(dog.getName() + " " + dog.getAge());
		dog.eat("s");

	}
}

在这里插入图片描述

4.2 接口中成员方法的特点

4.2.1 特点

(1)成员变量:

  • 接口中的成员变量只能是常量(final),并且默认修饰符为public static final。这意味着它们是公共的、静态的和不可修改的。
  • 声明时,通常会使用全大写字母加下划线来命名常量,例如:public static final int MAX_COUNT = 10;

(2)构造方法:

  • 接口中没有构造方法。因为接口主要定义了行为契约而不是实现细节,所以不需要构造方法。

(3)成员方法:

  • 在JDK 7及之前的版本中,接口中只能定义抽象方法,默认修饰符为public abstract。抽象方法没有具体实现,在实现类中必须重写这些抽象方法。
  • 在JDK 8之后,Java允许在接口中添加默认方法(default method)和静态方法(static method),这样就可以在接口中提供默认实现或直接调用静态方法。

以下是一个示例代码,展示如何定义一个包含常量和抽象方法的接口:

interface MyInterface {
    // 常量
    public static final int MAX_COUNT = 10;

    // 抽象方法
    public abstract void abstractMethod();
}

请注意,publicabstract 关键字在接口中可以省略,因为它们是默认的修饰符。同样地,在实现类中,可以将方法定义为公共和抽象的,并重写接口中的抽象方法。

4.2.2 验证

(1)准备代码

接口

public interface TryInterVar {
	int a=10;//没有明确的情况下,字段默认 public static final
	void method();//方法默认 public 
}

实现类

public class InterImpl implements TryInterVar{
	@Override
	public void method() {
		System.out.println("InterImpl Try");
	}
}

测试类

public class Test {
    public static void main(String[] args) {
        System.out.println(TryInterVar.a);
        //TryInterVar.a=20;//a是用final修饰的不可被修改

        // 创建实现类对象
        InterImpl I = new InterImpl();
        I.method();
        Scanner sc = new Scanner(System.in);
        sc.next();

    }
}
(2)过程

我们先保证sc.next();代码不会停下。

【1】在终端terminal中输入jps
在这里插入图片描述
【2】再输入jhsdb hsdb -> FIle ->选择第一个把Test(即需要查看的编码输入 20412)-> Tools
的第一个 ->把要查看的内容输入器名称(可以是类名也可以是接口名称)
在这里插入图片描述
【3】观察
在这里插入图片描述

4.3 类、接口之间的继承关系

(1)类和类:
继承(单继承,不能多继承,可多层继承
(2)类和接口
实现关系,可单多实现,还可以在继承一个类的同时实现多个接口
(3)接口和接口;
继承,可单可多,如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法

4.3.1 接口的多继承

public interface Inter1 {
	public abstract void method1();
	public abstract void method2();
	public abstract void method3();
}
public interface Inter2 {
	public abstract void method3();
	public abstract void method4();
	public abstract void method5();
}
public class InterImpl implements Inter1,Inter2{
    @Override
    public void method1() {  }
    @Override
    public void method2() {  }
    @Override
    public void method3() {  }
    @Override
    public void method4() {  }
    @Override
    public void method5() {  }
}

4.3.2 实现类实现了最下面的子接口

public interface Inter1 {
	public  abstract void method1() ;
}
public interface Inter2 {
	public  abstract void method2() ;
}
public interface Inter3 extends Inter1,Inter2 {
	public abstract void method3();
}

实现类

public class InterImpl  implements Inter3{
	@Override
	public void method1() {}
	@Override
	public void method2() {}
	@Override
	public void method3() {}
}

4.4 抽象类和接口存在一些显著的区别

在 Java 中,抽象类和接口都是用于实现多态性的机制。它们都可以被继承或者实现,并且都可以定义抽象方法.

  1. 抽象类可以有成员变量和非抽象方法的实现,而接口只能有常量和抽象方法。

抽象类是一个可以包含抽象方法(没有具体实现的方法)和具体方法的类。它可以有字段和构造函数。
接口只能包含常量、抽象方法和默认方法,不能包含字段和构造函数。

  1. 子类只能继承一个父类的行为,实现多个接口定义的行为。

  2. 抽象类的构造函数在子类中被调用时会默认先调用父类的构造函数,而接口不允许有构造函数。

  3. 接口中的所有方法都是公共的,而抽象类可以拥有公共、受保护和私有的方法。

  4. 抽象类可以提供默认实现,而接口不允许提供默认实现(Java 8 开始支持接口中的默认方法)。

  5. 抽象类适用于需要在多个类之间共享代码的场景,而接口则更适合于定义规范和实现多种形式的多态性。

抽象类用于描述具有共同特征的一组相关子类,它们通常共享某些公共行为。
接口用于定义一种契约或者协议,并强制要求所有实现该接口的类提供相应的行为。

4.5 新增方法

<1>JDK7 :接口只能定义抽象方法

接口内的规则一改增,下面的实现类也需要随之改增

<2>JDK8:接口可以有定义的方法体(默认,静态)

接口升级:实现类不需要立马修改,等带规则,重写就可以

(1)允许接口定义默认方法,default修饰
格式L:public default	返回值类型 方法名(参数列表){ 
实例L:public default void show(){}
	**注意事项
	[1]默认方法不是抽象方法,不强制被重写;如果被重写,需去掉default关键字
	[2]public可以省略,default不能省略,如果省略Java会将其当做抽象方法
	[3]如果实现多个接口,多个接口存在相同名字的默认方法,子类就必须对改方法进行重写
public interface InterA {
	void method();
	
	//public可以省略,但default 如果省略,java会怀疑是抽象方法,且抽象方法不能拥有方法体
	default void show() {
		System.out.println("Default--MethodA");
	}
}
public interface InterB {
//	如果实现多个接口,多个接口存在相同名字的默认方法,子类就必须对改方法进行重写
	public default void show() {
		System.out.println("Default--MethodB");
	}
}

实现类

public class InterImpl implements InterB ,InterA{

	@Override
	public void method() {
		System.out.println("實現可重寫方法");
	}
	//此处不可以加上default,如果要重寫就需要去除default
	
//	[3]如果实现多个接口,多个接口存在相同名字的默认方法,子类就必须对改方法进行重写
	@Override//此处必须重写
	public void show() {
		//InterA.super.show();
		System.out.println("重寫了接口中的default默認方法");
	}
}

测试类

public static void main(String[] args) {
	InterImpl  li =new InterImpl() ;
	li.method();
	li.show();
	}
}
(2)允许接口中定义定义静态方法,需要用static修饰
格式L:public static 返回值类型 方法名(参数列表){}
实例L:public static void show(){}
	**注意事项
	[1]静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
	[2]public可以省略,static不能省略。如果省略Java会将其当做抽象方法
	[3]静态方法不可重写

(1)接口

public interface Inter {
	 void method();
	 static void show() {
		System.out.println("Inter-static Method");
	}
}

(2)实现类不可被重写,需要去掉Override,只是一个重名的方法

public class InterImpl implements Inter{
	@Override
	public void method() {
		System.out.println("InterImpl-overideMethod");
	}
	//实现类不可被重写,需要去掉Override

	//不叫重写,只是一个重名的方法
	public  static void show() {
		System.out.println("InterImpl-static Method");
	}
}

(3)测试类
再次理解重写: 子类把父类继承下来的虚方法表里面的方法覆盖的过程。静态的,私有的,最终的不会添加到方法表的。

public class Test {
	public static void main(String[] args) {
//下面只是重名的方法:接口中静态方法
		Inter.show();
	//实现了的静态方法
		InterImpl.show();
	}
	//再次理解重写:	子类把父类继承下来的虚方法表里面的方法覆盖的过程。
	//静态的,私有的,最终的不会添加到方法表的。
}

<3>JDK9:接口有私有方法:

JDK9以前,一个接口在两个default方法,如果有相同的表述,可以采用在写一个default方法抽取出来,如何调用对应方法
	但抽取是为本代码访问,不想被外类使用,所以要变访问修饰符 -->抽离方法
	
	
 [1]普通的私有方法,给default默认方法服务
	格式1L:private 返回值类型 方法名(参数列表){}
	实例1L:private void show(){}



[2]静态的使用方法,给static静态方法服务
	格式2L:private static 返回值类型 方法名(参数列表){}
	实例2L:private static void method(){}

(1)接口

public interface Inter1 {
	 default void showA() {
		System.out.println("ShowB--run");
		RunA();
	 static  void showB() {
		System.out.println("ShowA--run");
		RunB();
	}
	}
	
//	public default void Run() {
//		System.out.println("Running");
//	}

	//普通的私有方法,给默认方法服务的。default删去
	private void RunA() {
		System.out.println("Running-defalult ");
	}
	
	//静态的私有方法,给静态方法服务的
	private static void RunB () {
		System.out.println("Running - static");
	}
}

(2)测试类

public class Inter1Test implements Inter1 {

    public static void main(String[] args) {
        // 调用静态方法 showB
        Inter1.showB();

        // 创建 Inter1Test 实例并调用默认方法 showA
        Inter1Test test = new Inter1Test();
        test.showA();
    }
}

在这里插入图片描述

4.6 应用

4.6.1 编译看左边,运行看右边

public interface Method{}

public void show(Method的接口 c){}

即:接口类型 x = new 实现类对象
(编译看左边,运行看右边)

1>接口代表规则,是行为的抽象。想要让那个类拥有一个行为,就让该类对应的接口
2>当一个方法的参数是接口时,可以传递接口所有实现类的对象–>接口多态

接口和抽象类不可以实例化,
是指不可以new一个接口或抽象类,
但是可以传递引用,实现多态

4.6.2 适配器模式 Design pattern

当一个接口中的抽象方法过多,但只需要使用其中一部分时候采用【相当于一个方法表】

步骤:
(1)编写中间类: 接口名Adapter 实现对应接口
(2)对接口中的抽象方法进行空实现
(3)让真正的实现类继承中间类,并重写需要的方法
(4)为了避免其他类创建适配器类的对象,中间的适配器类用abstract修饰

细节:继承的实现类还有多个继承,可以采取间接继承【因为Java没有多继承的】

五、Lambda表达式

Java的lambda表达式是一种匿名函数,它允许您像数据一样传递代码。它提供了一种简洁的方法来编写只有一个抽象方法的接口的实例。Lambda表达式通常用于函数式接口,这些接口只有一个抽象方法。

public class TestLambda{
    public static void main(String[] args) {
        //匿名内部类
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        r.run();
        

        //Lambda表达式
        Runnable r1 = () -> System.out.println(Thread.currentThread().getName());
        r1.run();
    }
}

5.1 基本语法

Lambda表达式的基本语法如下:

(parameters) -> expression

其中,parameters是函数的参数列表,expression是函数体。如果函数体有多行代码,可以使用大括号将其括起来,并使用return语句返回结果。

()->{
	
}

() – 对应方法的形参
-> – 固定格式
{} – 方法体

函数式编程:

忽略掉面向对象复杂的语法,强调做什么

函数式编程是一种编程范式,它将计算视为数学函数的求值,并避免改变状态和可变数据。函数式编程强调函数的纯粹性、不可变性和无副作用。在函数式编程中,函数被视为一等公民,可以作为参数传递给其他函数,也可以作为返回值。这种编程范式通常与不可变性、高阶函数和递归等概念结合在一起。

public class TestLambda {
    public static void main(String[] args) {
        // lambda
        //Java中的匿名内部类可以直接调用外部类的成员变量或方法,
        //即使没有显式调用也可以访问。这是因为匿名内部类会隐式持有对外部类的引用,所以可以直接访问外部类的成员。
        method(() -> System.out.println("我是匿名内部类"));
    }

    public static void method(Swim swim) {
        swim.swiming();
    }
}

// 接口只有一个抽象方法。
interface Swim {
    void swiming();
}

注意事项

  • Lambda表达式是简化匿名内部类的一种方式,适用于函数式接口。
    • 如果一个接口中存在多个抽象方法,编译器会报错。
  • 函数式接口是只有一个抽象方法的接口,可以使用@FunctionalInterface注解来标记,但不是必需的。

5.2 省略规则

(1)小括号:数据类型可以省略,如果参数只有一个,小括号还可以省略
(2)大括号:如果方法体只有一行, return,分号,大括号都可以省略,如果省略就需要同时省略。

例如:

Integer [] arr= {9,2,3,1,4,5,6,7,8};
Arrays.sort(arr1,(Integer a,Integer b)->{
     return a-b;
});

可被省略为

Integer [] arr= {9,2,3,1,4,5,6,7,8};
Arrays.sort(arr,(a, b)->a-b);
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你会魔法吗✧(≖ ◡ ≖✿)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值