java基础-面向对象

目录

一、基础原理

1.1、java的运行机制

1.2、内存和内存地址

1.3、变量和方法的内存分配

1.4、数组的内存分配

二、面向对象进阶

2.1、静态变量(static)

2.1.1、static修饰成员变量

2.1.2、static修饰成员方法(工具类)

2.1.3、注意事项

2.2、final关键字

2.3、枚举(enum)

2.4、封装

2.5、实体类(javabean类)

三、面向对象高级

3.1、继承(extends)

3.1.1、继承的特点

3.1.2、继承中成员变量的特点

3.1.3、继承中成员方法的特点

3.1.4、继承中构造方法的特点

3.1.5、总结this和super关键字

3.1.6、方法的重写(@Override)

3.1.7、带有继承结构的标准javabean类

3.1.8、子类到底能继承父类的哪些内容

3.2、多态

3.2.1、多态好处与存在的问题

3.2.2、多态下的类型转换

3.2.3、实战--支付模块(lombok技术)

3.3、抽象类(abstract)

3.4、接口(interface)

3.5、抽象类与接口区别

3.6、类中的成分---代码块

3.7、内部类(x4)

3.8、匿名内部类(没有名字的一次性内部类)

3.9、函数式编程---lambda

3.10、函数式编程---方法引用

3.11、常用API

3.11.1、String

3.11.2、String生成验证码

3.11.3、ArrayList集合

3.11.4、String与ArrayList区别

在此之前,你需要了解java基础语法:

https://blog.csdn.net/qq_44930306/article/details/155890237?spm=1011.2415.3001.5331

项目在VSCode运行需要在扩展商店搜索 Java Extension Pack → 安装

一、基础原理

1.1、java的运行机制

(1)、java程序运行的过程是怎么样的?

解答:Java文件 -->(编译为)class字节码文件 -->将结果(运行)在虚拟机上。
(2)、Java是直接运行在操作系统里的吗?

解答:不是,运行在虚拟机里。
(3)、虚拟机的好处是什么?

解答:可以跨平台。
(4)、为什么要跨平台?

解答:写一套代码,可以在任意的操作中运行,大大降低了开发成本。

1.2、内存和内存地址

内存:软件在运行时,用来临时存储数据

内存地址:内存中每一个小格子的编号,为了快速管理内存空间

1.3、变量和方法的内存分配

java 复制代码
    public static void main(String[] args) {
        int a = 10;// 在栈内存中为变量 a 分配空间,存储值 10
        int b = 20; // 在栈内存中为变量 b 分配空间,存储值 20
        System.out.println("交换前:" + a + "," + b); // 交换前:10,20
        change(a, b);
        System.out.println("交换后:" + a + "," + b); // 交换后:10,20
    }

    public static void change(int a, int b) {
//        交换变量
        int temp = a;
        System.out.println(temp); // 10
        a = b;
        System.out.println(a); // 20
        b = temp;
        System.out.println(b); // 10
    }
//    Java 中基本数据类型(如 int)的参数传递是值传递,不是引用传递
//    change 方法内部的变量交换只影响方法内部的局部变量
//    原始的 a 和 b 变量的值不会被改变,因为它们是通过值传递的

//    Java中基本数据类型是值传递,无法直接修改原始变量的值
//    数组和对象是引用传递,可以修改其内容

(1)、Java虚拟机把内存分成了几个部分?

解答:栈、堆、方法区、本地方法栈、程序计数器
(2)、栈、堆、方法区的作用?

解答:
栈内存=程序的主入口(main方法):开始执行是会进栈,代码执行完毕会出栈
堆内存=new关键字:在堆内存开辟空间并产生地址
方法区:存储字节码信息
(3)、基本数据类型在内存中的特点?

解答:记录的是真实数据,传递的也是真实数据
(4)、引用数据类型在内存中的特点?

解答:记录的是地址值,传递的也是地址值

1.4、数组的内存分配

java 复制代码
    public static void main(String[] args) {
//        在堆内存中创建一个包含5个整数的数组对象
//        在栈内存中为变量 arr 分配空间,存储指向堆中数组对象的引用
        int[] arr = {1, 2, 3, 4, 5};
        System.out.println("交换前");
        printArray(arr); // 1 2 3 4 5
//        交换数组首位元素
        change(arr);
        System.out.println("交换后");
        printArray(arr); // 5 2 3 4 1
    }

    public static void change(int[] arr) {
        int temp = arr[0];
        arr[0] = arr[arr.length - 1];
        arr[arr.length - 1] = temp;
    }

    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

数组是引用类型:arr 变量存储的是指向堆中数组对象的引用。

方法参数传递:change 和 printArray 方法接收的是数组的引用,可以修改数组内容。
堆内存共享:所有对 arr 引用的操作都作用于同一个堆内存中的数组对象。
栈内存独立:每个方法的局部变量都在各自的栈帧中,互不影响。

二、面向对象进阶

2.1、静态变量(static)

Static:表示静态,是Java的修饰符,用来修饰(成员方法/成员变量)。

2.1.1、static修饰成员变量

特点:

(1)、叫做静态变量,被该类所有对象共享

(2)、不属于对象,属于类

(3)、随着类的加载而加载,优先于对象而存在。

(4)、只要赋值一次(只要有一个对象修改了静态变量的值),其他对象再次访问时就是修改之后的结果

调用方式一:类名调用(推荐)

调用方式二:对象名调用

案例:有多个学生,最初都是小文老师教,后来有学生要申请换成大王老师

java 复制代码
public class Student {
    String name;
    int age;
    //    共享一个老师
    static String teacherName;
}
java 复制代码
public class Test {
    public static void main(String[] args) {
        Student.teacherName = "小文老师";
//        创建一个学生对象
        Student s1 = new Student();
        s1.name = "张三";
        s1.age = 18;
//        创建第二个学生
        Student s2 = new Student();
        s2.name = "李四";
        s2.age = 19;
        s2.teacherName = "小王老师"; // 加完之后,结果里都变成了小王老师
        System.out.println(s1.name + ":" + s1.age + ":" + Student.teacherName); // 张三:18:小文老师
        System.out.println(s2.name + ":" + s2.age + ":" + Student.teacherName); // 李四:19:小文老师
    }

2.1.2、static修饰成员方法(工具类)

static修饰的方法叫做静态方法,该方法多用在测试类和工具类中,javabean类中很少会用。
工具类 :不是用来描述一类事物的,也没有main方法,而是帮我们做一些事情的类。

要求:类名见名知意、私有化构造方法、方法定义为静态。

java 复制代码
public class ArrayUitl {
    //    私有化构造方法,目的:不让外界创建对象
    private ArrayUitl() {
    }

    //    定义方法(静态)
//    1、提供一个方法printArray,用于打印数组【10,20,30,40】
    public static void printArray(int[] arr) {
        System.out.print("[");
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                System.out.print(arr[i] + "]");
            } else {
                System.out.print(arr[i] + ", ");
            }
        }
    }
//    2、就数组平均值
    public static double getAverage(int[] arr) {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum * 1.0 / arr.length;
    }
java 复制代码
public class Test {
    public static void main(String[] args) {
        int[] nums = {10, 20, 30, 40};
        ArrayUitl.printArray(nums);

        double avg =ArrayUitl.getAverage(nums);
        System.out.println("平均值是:" + avg); // 平均值是:25.0
    }

2.1.3、注意事项

(1)、静态方法只能访问静态变量和其他的静态方法(静态只能调用静态);

(2)、**非静态方法(可以调用所有)**可以访问静态变量或者静态方法,也可以访问非静态的成员变量和成员方法;

(3)、静态方法中没有this关键字

2.2、final关键字

final:表示最终,不可变。可以修饰变量、类、方法。

final int a=10; // final修饰的变量不能被修改

final Student stu=new Student(); // final修饰的引用不能被修改

java 复制代码
public class Circle {
    private double radius;
    private final double PI = 3.14;

    public Circle() {
    }

    public Circle(double radius) {
        this.radius = radius;
    }
    public double getPI() {
        return PI;
    } // PI被final修饰,故而没有必要提供setPI方法

    public double getRadius() {
        return radius;
    }
    public void setRadius(double radius) {
        this.radius = radius;
    }

    //    行为
    public double findArea() {
        return PI * radius * radius;
    }
    public double findPerimeter() {
        return 2 * PI * radius;
    }
}
java 复制代码
public class Test {
public static void main(String[] args) {
    Circle c1 = new Circle(1.5);
    System.out.println(c1.getPI()); // 3.14
    System.out.println(c1.getRadius()); // 1.5
//  获取圆的面积和周长
    System.out.println(c1.findArea()); // 7.0649999999999995
    System.out.println(c1.findPerimeter()); // 9.42
}
}

2.3、枚举(enum)

枚举是一个特殊的Javabean类,这个类的对象是有限个。

使用场景:订单状态、月份、星期、游戏角色职业、会议室预约状态、设备状态。
注意事项:

(1)每一个枚举项,都是该枚举类的对象,每一个对象都是通过构造方法创建出来的

(2)枚举项在底层其实就是常量,默认用public static final修饰

(3)枚举类的第一行上必须是枚举项,枚举项之间用逗号隔开,以分号作为结尾

(4)枚举类的构造方法必须是private修饰,不让外界创建本类的对象

(5)编译器会给枚举类新增两个默认存在的方法:values()、valueOf()

java 复制代码
public enum OrderState {
//    电商订单有以下6种状态
    PAYMENT_PENDING("待支付"),
    PROCESSING("处理中"),
    SHIPPED("已发货"),
    OUT_FOR_DELIVERY("配送中"),
    DELIVERED("已送达"),
    CANCELED("已取消");

    private String name;
    private OrderState(String name) {
        System.out.println("每次都会执行");
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
java 复制代码
public class Test {
    public static void main(String[] args) {
        OrderState o1 = OrderState.OUT_FOR_DELIVERY;
        System.out.println(o1.getName());

        switch (o1) {
            case PAYMENT_PENDING -> System.out.println("待支付状态");
            case PROCESSING -> System.out.println("订单在处理中");
            case SHIPPED -> System.out.println("订单已发货");
            case OUT_FOR_DELIVERY -> System.out.println("订单正在配送中"); // 执行这里
            case DELIVERED -> System.out.println("订单已送达");
            case CANCELED -> System.out.println("订单已取消");

        }
//      values() 获取本类所有的枚举项
        OrderState[] values = OrderState.values();
        for (OrderState value : values) {
            System.out.println(value.getName());
        }
//      valueOf() 获取指定名称的枚举项
        System.out.println(OrderState.valueOf("SHIPPED"));

    }
}

2.4、封装

1、含义:用类设计对象处理某一个事物的数据时,应该要把处理的数据以及处理这些数据的方法,设计到一个对象中去,类就是一种封装

2、设计规范:合理隐藏、合理暴露。

3、代码层面如何对对象的成员进行公开或隐藏
隐藏成员:使用private关键字修饰成员变量,就只能在本类中被直接访问,其他任何地方不能直接访问;
公开成员:使用public修饰的get和set方法合理暴露成员变量的取值和赋值。

4、参考案例:score在Test里被赋值负数,所以在get/set里进行判断再暴露出去。

2.5、实体类(javabean类)

要求1:类中的成员变量全部私有,并提供public修饰的getter/setter方法

要求2:类中需要提供一个无参构造器。有参构造器可选

它仅仅只是一个用来保存数据的java类,可以用它创建对象,保存某个事物的数据,例如以上案例的Student、Circle类等。

三、面向对象高级

3.1、继承(extends)

含义:面向对象三大特征之一,可以让类跟类之间产生父子关系

作用---操作:多个子类中重复的代码抽取到父类,子类可以直接使用。

作用---好处:减少代码冗余,提高代码复用性。
语法格式public class 子类 extends 父类 {...}
继承后子类的好处

好处1:子类可以得到父类的属性和行为,子类可以使用

好处2:子类可以在父类的基础上,增加其他功能,使子类更强大

3.1.1、继承的特点

(1)、java只支持单继承,不支持多继承,但支持多层继承

单继承:一个子类只能继承一个父类

不能多继承:子类不能同时继承多个父类

多层继承:子-父-爷

(2)、直接继承的父类叫做直接父类,间接继承的爷爷叫做间接父类

(3)、Java中的顶级父类为Object,每一个类都直接或者间接的继承于Object

3.1.2、继承中成员变量的特点

书写规则:把多个子类中相同的属性抽取到父类当中
遵守就近原则--先在局部位置找,本类成员位置找,父类成员位置找,逐级往上。

如果出现了重名的成员变量怎么办?

解答:找子类的变量用this.xx,找父类的变量用super.xx

java 复制代码
public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.Show();
    }
}
class Fu {
    String name = "父类name";
}
class Zi extends Fu {
    String name = "子类name";
    public void Show() {
        String name = "静态方法";
        System.out.println(name); // 方法里的name
        System.out.println(this.name); // 子类name
        System.out.println(super.name); // 父类name
    }
}

3.1.3、继承中成员方法的特点

书写规则:把多个子类中共性的成员方法抽取到父类当中

调用规则:遵守就近原则
this调用**:先访问本类,再访问父类**
super调用**:直接访问父类**

java 复制代码
public class Test {
    public static void main(String[] args) {
//        情况一:外界创建子类的对象,并调用方法
        Zi1 z = new Zi1();
        z.drink();
        z.eat();

    }

}
class Fu1 {
    public void eat() {
        System.out.println("吃吃吃");
    }

    public void drink() {
        System.out.println("喝水");
    }
}
class Zi1 extends Fu1 {
//    情况二:本类中,调用其他方法
    public void lunch(){
//        这三种都能拿到父类的方法
        drink();
        this.eat();
        super.eat();
    }
}

3.1.4、继承中构造方法的特点

(1)、父类中的构造方法不会被子类继承,只能通过super关键字调用

(2)、如果子类的构造方法不写super,JVM也会加一个默认的super(),先访问父类的无参构造

(3)、如果想要访问父类有参构造,必须手动书写super(参数)

(4)、Super()必须卸载构造方法第一行,先执行父类的构造,再执行子类的构造

3.1.5、总结this和super关键字

this内存角度:表示当前方法调用者的地址值
this代码角度:用它直接调用本类成员(成员变量、成员方法、构造方法)
super关键字:代表使用父类中的内容

3.1.6、方法的重写(@Override)

方法重写**:在子类中,把父类的方法再写一遍,方法申明保持一致**。

使用场景:如果父类的方法不能满足子类的要求了,子类中可以把该方法再重写一遍

注意事项(要求):

(1)、重写方法的名称、形参列表必须与父类中一致,方法体按照实际需求书写

(2)、重写的方法申明与父类保持一致即可

(3)、final修饰类为最终类 ,里面所有的方法不能被重写

(4)、private私有方法、static静态方法、final最终方法不能被重写

案例:模拟手机功能升级--第一代手机:只能打电话、第二代手机:打电话、发短信、第三代手机:打电话升级为视频通话、发短信、玩游戏。

3.1.7、带有继承结构的标准javabean类

参考以下案例:

第一步:利用画图法解决

第二步:分类

第三步:抽取共性的内容不断往上抽取(从下往上画)

第四步:写代码(从上往下写)

java 复制代码
public class Express {
    protected String sender;
    protected int weight;
    protected String receiver;
    public Express(String sender, int weight, String receiver) {
        this.sender = sender;
        this.weight = weight;
        this.receiver = receiver;
    }
    // 计算基础运费
    public double calculateFee() {
        return weight * 10; // 每公斤10元
    }
}

java 复制代码
public class Test {
    public static void main(String[] args) {
        Express e = new Express("EM001345", 5, "王五收");
        SameCityExpres s = new SameCityExpres("SM003476", 8, "王五收");
        AirExpress a = new AirExpress("AM007483", 20, "王五收");
        System.out.println(e.sender + " " + e.weight + " " + e.receiver);
        // 计算费用
        System.out.println("基础快递费用:" + e.calculateFee()); // 基础快递费用:50.0=5*10
        System.out.println("同城快递费用:" + s.calculateFee()); // 基础快递费用:90.0=8*10+10
        System.out.println("异地空运费用:" + a.calculateFee()); // 基础快递费用:215.0=20*10+15

    }
}

3.1.8、子类到底能继承父类的哪些内容

(1)、构造方法 :不能被子类继承,可以利用super关键字调用

(2)、成员变量可以被子类继承,private私有的也可以,但是私有的无法直接调用

(3)、成员方法虚方法(普通的成员方法,非final、非static、非private修饰的方法)可以被继承

(4)、final修饰的最终方法不能被继承,可以被调用;

(5)、static修饰的静态方法不能被继承,可以被调用;

(6)、private修饰的私有方法不能被继承,不能被调用

方法重写:子类替换虚方法表里面方法的地址值

3.2、多态

多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。

代码体现:

People p1=new Student();

p1.run();

People p2=new Teacher();

p2.run();
前提有继承/实现关系存在父类引用子类对象存在方法重写

3.2.1、多态好处与存在的问题

好处:

(1)在多态形式下,右边对象是解耦合的,更便于扩展和维护

(2)定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利
存在问题:
多态下不能使用子类的独有功能,需要强制类型转换

3.2.2、多态下的类型转换

**自动类型转换:****父类 变量名=new 子类();****例如:**People p = new Teacher();
**强制类型转换:****子类 变量名=(子类) 父类变量;****例如:**Teacher t = (Teacher)p;

它们可以把对象转换成真正的类型,从而解决了多态下不能调用子类独有方法的问题
注意事项:

(1)存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错

(2)运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException)的错误

(3)强转前,建议使用instanceof关键字,判断当前对象的真实类型,再进行强转 例如:p instanceof Student

3.2.3、实战--支付模块(lombok技术)

java 复制代码
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
//        1、创建卡片类,以便创建金卡或者银卡对象,封装车主数据
//        2、定义一个卡片父类:Card,定义金卡和银卡的共同属性和方法
//        3、定义一个金卡类,继承卡片类,金卡必须重写消费方法(8折优惠),可打印洗车票
       GoldCard goldCard = new GoldCard("A4394MM", "张三", "18234564321", 5000);
//        4、定义一个银卡类,继承卡片类,银卡必须重写消费方法(9折优惠)
       SilverCard silverCard = new SilverCard("A4876MM", "李四", "18232164321", 2000);
//        5、办张金卡:创建对象,交给一个独立的业务(支付机)来完成:存款、消费
//        6、办张银卡:创建对象,交给一个独立的业务(支付机)来完成:存款、消费
       pay(goldCard);
       pay(silverCard);
    }
   public static void pay(Card card) {
       Scanner sc = new Scanner(System.in);
       System.out.println("请输入您当前消费的金额:");
       double money = sc.nextDouble();
       card.consume(money);
   }
}
java 复制代码
public class GoldCard extends Card {
   public GoldCard(String cardId, String name, String phone, double money) {
       super(cardId, name, phone, money);   // 4 参
   }
   @Override
   public void consume(double money) {
       double realPay = money * 0.8;
       if (realPay <= getMoney()) {
           setMoney(getMoney() - realPay);
           System.out.println("消费成功,余额为:" + getMoney());
           if (realPay >= 200) {
               printWashTicket();
           } else {
               System.out.println("您当前消费不满200,不能免费洗车");
           }
       } else {
           System.out.println("余额不足");
       }
   }
   /* 打印洗车票 */
   public void printWashTicket() {
       System.out.println("打印洗车票");
   }
}
java 复制代码
public class SilverCard extends Card {
   public SilverCard(String cardId, String name, String phone, double money) {
       super(cardId, name, phone, money);   // 4 参
   }
   @Override
   public void consume(double money) {
       if (money <= getMoney()) {
           setMoney(getMoney() - money * 0.9);
           System.out.println("消费成功,余额为:" + getMoney());
       } else {
           System.out.println("余额不足");
       }
   }
}
java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor          // 无参构造
@AllArgsConstructor         // 全参构造(4 个参数)
public class Card {
   private String cardId;
   private String name;
   private String phone;
   private double money;
   /* 预存金额 */
   public void deposit(double money) {
       this.money += money;
   }
   /* 消费金额 */
   public void consume(double money) {
       if (money <= this.money) {
           this.money -= money;
           System.out.println("消费成功,余额为:" + this.money);
       } else {
           System.out.println("余额不足");
       }
   }
}

扩展: lombok技术可以实现为类自动添加getter和setter方法 、无参数构造器、toString方法等

Alt+回车键:将'lombok'添加到类路径中、建议org.projectlombok:lombok:1.18.30,最后在编译器的设置里

File → Settings → Build, Execution, Deployment → Compiler → Annotation Processors → Enable annotation processing

也可以在VScode下载lombok.jar放在项目的lib文件夹:https://projectlombok.org/download

可以建一个settings.json再进行运行:

javascript 复制代码
{
  "java.project.referencedLibraries": [
    "lib/lombok.jar"
  ]
}

3.3、抽象类(abstract)

抽象类、抽象方法都是用abstract修饰的;抽象方法只有方法签名,不能写方法体。

注意事项(特点):

(1)、抽象类中可以不写抽象方法,但有抽象方法的类必须是抽象类;

(2)、类有的成员:成员变量、方法、构造器,抽象类都具备;

(3)、最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现;

(4)、一个类继承抽象类,必须重写抽象类的全部抽象方法,否则这个类也必须定义成抽象类。


好处:

父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现 。设计这样的抽象类,就是为了更好的支持多态

模板方法设计模式

建议使用final关键字修饰模板方法,为什么?

解答:模板方法是给子类直接使用的,不能被子类重写;

一旦子类重写了模板方法,模板方法就失效了。

3.4、接口(interface)

注意:接口不能创建对象
接口是用来被类实现(implements)的,实现接口的类称为实现类,一个类可以同时实现多个接口,这里实现类多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。
好处:

弥补了类单继承的不足,一个类同时可以实现多个接口,使类的角色更多,功能更强大。

JDK8开始,接口新增了哪些方法?

1、默认方法default :使用实现类的对象调用

2、静态方法static :必须用当前接口名调用

3、私有方法private:jdk9开始才有的,只能在接口内部被调用

新增的这些方法,增强了接口的能力,更便于项目的扩展和维护。

注意事项:

(1)、接口与接口可以多继承:一个接口可以同时继承多个接口

(2)、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现;

(3)、一个类继承了父类,又同时实现了接口,如果父类中和接口中有同名的默认方法,实现类会优先用父类的

(4)、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可

案例---定义接口

java 复制代码
package Interface2;
//  usb充电器接口
public interface UsbCharger {
  void charge();// 抽象方法: 充电
  // 默认方法:检查充电状态
  default String checkStatus() {
    return "检查充电状态...";
  }
  double VOLTAGE = 5.0;// 常量:电压
}

实现接口 - 不同设备(Phone+Tablet)

java 复制代码
package Interface2;
public class Phone implements UsbCharger {
    private String brand;
    public Phone(String brand) {
        this.brand = brand;
    }
    // 必须实现UsbCharger接口的抽象方法
    @Override
    public void charge() {
        System.out.println(brand + "手机正在充电...");
        System.out.println(VOLTAGE + "V" + "的电压");
    }
    // 手机特有的方法
    public void makeCall(String number) {
        System.out.println(brand + "手机正在拨打电话给 " + number);
    }
}
java 复制代码
package Interface2;
public class Tablet implements UsbCharger {
    private String model;
    public Tablet(String model) {
        this.model = model;
    }
    // 必须实现UsbCharger接口的抽象方法
    @Override
    public void charge() {
        System.out.println(model + "平板电脑正在充电...");
    }
    // 重写默认方法
    @Override
    public String checkStatus() {
        return model + "平板电脑充电状态良好。";
    }
    // 平板电脑特有的方法
    public void watchVideo() {
        System.out.println(model + "平板电脑正在观看视频: ");
    }
}

使用接口 - 充电站

java 复制代码
package Interface2;
public class ChargingStation {
  public static void main(String[] args) {
    // 1、使用接口类型声明变量
    UsbCharger device1 = new Phone("小米");
    UsbCharger device2 = new Tablet("MacBook");
    // 2、调用接口方法(多态)
    device1.charge(); 
    device2.charge();
    // 3、调用默认方法
    System.out.println(device1.checkStatus());
    System.out.println(device2.checkStatus());
    // 4、访问接口常量
    System.out.println(UsbCharger.VOLTAGE + "V");
    // 5、类型转换获取具体功能
    if (device1 instanceof Phone) {
      Phone phone = (Phone) device1; // 向下转型
      phone.makeCall("123456789");
    }
    // 6、数组中的多态应用
    UsbCharger[] devices = { device1, device2 };
    for (UsbCharger device : devices) {
      device.charge();
    }
  }
}

输出结果:

3.5、抽象类与接口区别

相同点:

1、都是抽象形式,都可以有抽象方法,都不能创建对象

2、都是派生子类形式:抽象类是被子类继承使用,接口是被实现类实现

3、一个类继承抽象类**/** 实现接口,都必须重写完它们的抽象方法,否则自己要成为抽象类**/** 报错

4、都能支持多态和实现解耦合
不同点:

1、抽象类中可以定义类的全部普通成员,接口只能定义常量和抽象方法(jdk8新增3种方式)

2、抽象类只能被类单继承,接口可以被类多实现

3、一个类继承抽象类就不能再继承其他类,一个类实现了接口(还可以继承其他类或者实现其他接口)

4、抽象类体现模板思想:更利于做父类,实现代码的复用性---最佳实践

5、接口更适合做功能的解耦合:解耦合性更强更灵活---最佳实践

|-------|----------------------------|--------------|
| 特性 | 接口 | 抽象类 |
| 多重继承 | 一个类可实现多个接口 | 只能继承一个抽象类 |
| 方法实现 | java8+支持默认方法 | 支持具体方法和抽象方法 |
| 构造方法 | 不能有 | 可以有 |
| 成员变量 | 只能是常量(public static final) | 可以有各种类型的成员变量 |
| 访问修饰符 | 默认public | 可以是各种访问修饰符 |
| 设计目的 | 定义行为契约、API | 代码复用、模板方法模式 |

3.6、类中的成分---代码块

代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)

分为静态代码块和实例代码块两种:
静态代码块:

格式:static {}

特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次

作用:完成类的初始化 ,例如:对静态变量的初始化赋值。
实例代码块:

格式:{}

特点:每次创建对象时,执行实例代码块,并在构造器前执行

作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值

3.7、内部类(x4)

如果一个类定义在另一个类的内部,这个类就是内部类。
1、成员内部类(静态内部类)

含义:就是类中的一个普通成员,类似前面我们学过的普通成员变量、成员方法。
外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...);

成员内部类的实例方法中,访问其他成员有啥特点?

可以直接访问外部类的实例成员、静态成员

可以拿到当前外部类对象,格式是:外部类名.this

2、静态内部类(有static修饰的内部类 )
外部类名.内部类名 对象名 = new 外部类.内部类(...);

可以直接访问外部类的静态成员,不能直接访问外部类的实例成员

java 复制代码
public class ComparisonDemo {
  public static void main(String[] args) {
    // 1、创建外部类实例
    OuterClass outer = new OuterClass();
    // 2、创建非静态内部类实例(需要外部类实例)
    OuterClass.InnerClass inner = outer.new InnerClass();
    inner.innerMethod();
    // 3、创建静态内部类实例(不需要外部类实例)
    OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
    staticInner.staticInnerMethod();
  }
}
java 复制代码
public class OuterClass {
  private String outerField="外部类字段";
  private static String staticOuterField="外部类静态字段";
  //非静态内部类
  class InnerClass{
    public void innerMethod(){
      // 可以访问外部类的成员
      System.out.println("访问外部类的非静态字段1:"+outerField);
      System.out.println("访问外部类的静态字段1:"+staticOuterField);
    }
  }
// 静态内部类
  static class StaticInnerClass{ 
    public void staticInnerMethod(){
      // 只能访问外部类的静态字段。不能访问非静态字段
      // System.out.println("访问外部类的非静态字段:"+outerField); // 这一行会报错
      System.out.println("访问外部类的静态字段2:"+staticOuterField);
    }
  }
}

3、局部内部类

它是定义在方法中、代码块中、构造器等执行体中,了解即可,为了引出匿名内部类

3.8、匿名内部类(没有名字的一次性内部类)

1、匿名内部类的书写格式是什么样的?
new 父类/接口() {
// 实现抽象方法
// 可以添加新的方法和字段
// 不能有构造方法
};

2、匿名内部类有啥特点?

解答:它本质就是一个子类,并会立即创建出一个子类对象。
3、基本作用?

解答:更方便的创建出一个子类对象
案例一:创建一个按钮,并为按钮添加一个点击事件监听器

java 复制代码
// 定义点击监听接口
interface onClickListener {
  void onClick();
}
// 使用匿名类实现接口
public class ButtonDemo {
  public static void main(String[] args) {
    Button button = new Button(); // 创建按钮对象
    // 使用匿名内部类设置点击监听器
    button.setOnClickListener(new onClickListener() {
      // 实现接口的抽象方法
      @Override
      public void onClick() {
        System.out.println("点击了按钮");
      }
    });
    button.click(); // 模拟按钮点击
  }
}
// 按钮类
class Button {
  private onClickListener listener;
  public void setOnClickListener(onClickListener listener) {
    this.listener = listener;
  }
  public void click() {
    if (listener != null) {
      listener.onClick();
    }
  }
}

案例二:继承类 - 线程创建

java 复制代码
// Thread(线程) 是 Java 中代表执行线程的类。一个程序可以同时运行多个线程。
// Runnable 是一个接口,定义了线程要执行的任务:
// 线程演示类
public class ThreadDemo {
  public static void main(String[] args) {
    // 方式1:继承Thread类(匿名内部类)
    Thread thread = new Thread() {
      @Override
      public void run() {
        System.out.println("线程1运行中: " + Thread.currentThread().getName()); // 输出 Thread-0
      }
    };
    // 方式2:实现Runnable接口(匿名内部类)
    Thread thread2 = new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println("线程2运行中: " + Thread.currentThread().getName()); // 输出 Thread-1
      }
    });
    thread.start(); // 启动线程
    thread2.start(); // 启动线程
  }
}

案例三:排序比较器

java 复制代码
import java.util.*;
public class SortDemo {
  public static void main(String[] args) {
    List<Student> students = new ArrayList<>();
    students.add(new Student("Alice", 22));
    students.add(new Student("Bob", 20));
    students.add(new Student("Charlie", 23));
    System.out.println("排序前:");
    students.forEach(System.out::println);// 会对每个 Student 调用 toString() 并换行显示
    // 使用匿名类实现Comparator接口进行排序
    Collections.sort(students, new Comparator<Student>() {
      @Override
      public int compare(Student s1, Student s2) {
        return Integer.compare(s1.getAge(), s2.getAge());
      }
    });
    System.out.println("排序后:");
    students.forEach(System.out::println);
  }
}

// 学生类
class Student {
  private String name;
  private int age;
  public Student(String name, int age) {
    this.name = name;
    this.age = age;
  }
  public String getName() {
    return name;
  }
  public int getAge() {
    return age;
  }
  @Override
  public String toString() {
    return "Student{name='" + name + "', age=" + age + "}";
  }
}

注意事项

1、只能 new 一次

没有名字 → 无法再次 new,也无法声明构造器(但可以用初始化块)。

2、this 的指向

在匿名类内部,this 指的是匿名类自己;想引用外部类要写 外部类名.this。

3、不能定义静态成员

匿名内部类里不允许出现 static 字段或方法(static final 编译期常量除外)。

4、对外部局部变量的修改

由于"值复制"机制,外部方法里再改变量,匿名类里不会感知

如果确实需要"双向"通信,可把变量包进数组或 AtomicXXX。

5、内存泄漏

匿名类对象长期持有外部类引用(非 static 场景),小心注册监听器后忘 remove。

3.9、函数式编程---lambda

lambda表达式:JDK8开始新增的一种语法形式,它表示函数

可以用于替代某些匿名内部类对象,从而让程序更简洁,可读性更好

注意:Lambda表达式只能替代函数式接口的匿名内部类

函数式接口:

(1)、有且仅有一个抽象方法的接口

(2)它上面可能会有一个@FunctionalInterface的注解,该注解用于约束当前接口必须是函数式接口

省略规则

(1)、参数类型全部可以省略不写

(2)、如果只有一个参数,参数类型省略的同时"()"也可以省略,但多个参数不能省略"()"

(3)、如果lambda表达式中只有一行代码,大括号可以不写,同时要省略分号";"如果这行代码是return语句,也必须去掉return

java 复制代码
import java.util.Arrays;
import java.util.List;
public class Lambdademo {
public static void main(String[] args) {
	List<String> fruits=Arrays.asList("Apple","Banana","Orange","Mango");
  // 使用匿名内部类遍历集合
  fruits.forEach(new java.util.function.Consumer<String>() {
    @Override
    public void accept(String fruit) {
      System.out.println("水果1:"+fruit);
    }
  });
  System.out.println("--------");
  // 使用Lambda表达式遍历集合
  fruits.forEach(fruit -> System.out.println("水果2:"+fruit));
  System.out.println("--------");
  // 使用方法引用遍历集合
  fruits.forEach(System.out::println);
  System.out.println("--------");
}
}

3.10、函数式编程---方法引用

简单理解:方法引用就是Lambda表达式的"语法糖",让代码更简洁。当Lambda只是调用一个现有方法时,就可以使用方法引用。

静态方法引用:类名::静态方法 → 调用类方法

实例方法引用:对象::实例方法 → 调用某个对象的方法

特定类型引用:类名::实例方法 → 调用参数对象的实例方法

构造器引用:类名::new → 创建新对象

java 复制代码
package neibulei;
import java.util.*;
import java.util.function.*;

public class MethodReferenceExplanation {
 public static void main(String[] args) {
        System.out.println("=== 四种方法引用详细对比 ===\n");
        // 准备数据
        List<String> names = Arrays.asList("张三", "李四", "王五", "赵六");
        System.out.println("1. 静态方法引用 (类名::静态方法名)");
        System.out.println("格式: ClassName::staticMethodName\n");
        // 示例:将字符串转换为大写
        System.out.println("示例: 转大写");
        // Lambda表达式写法
        names.stream()
            .map(s -> s.toUpperCase())  // Lambda
            .forEach(s -> System.out.print(s + " "));
        
        System.out.println("\n↓ 使用静态方法引用 ↓");
        // 静态方法引用写法(String类的toUpperCase是实例方法,不能这样用)
        // 正确示例:使用自定义的静态方法
        names.stream()
            .map(String::toUpperCase)  // 这是特定类型的实例方法引用,不是静态方法引用!
            .forEach(System.out::println);
        
        // 真正的静态方法引用示例
        System.out.println("\n真正的静态方法引用:");
        names.forEach(MethodReferenceExplanation::printName);

        System.out.println("\n\n2. 实例方法引用 (对象::实例方法名)");
        System.out.println("格式: instance::instanceMethodName\n");
        
        // 创建一个前缀处理器
        PrefixProcessor processor = new PrefixProcessor("学生-");
        
        // Lambda表达式写法
        System.out.println("Lambda表达式:");
        names.stream()
            .map(name -> processor.addPrefix(name))
            .forEach(System.out::println);
        
        // 实例方法引用写法
        System.out.println("\n实例方法引用:");
        names.stream()
            .map(processor::addPrefix)
            .forEach(System.out::println);

        System.out.println("\n\n3. 特定类型的方法引用 (类名::实例方法名)");
        System.out.println("格式: ClassName::instanceMethodName\n");
        
        // 示例:字符串比较
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
        
        System.out.println("按字母顺序排序:");
        
        // Lambda表达式写法
        fruits.stream()
            .sorted((s1, s2) -> s1.compareTo(s2))
            .forEach(s -> System.out.print(s + " "));
        
        System.out.println("\n↓ 使用方法引用 ↓");
        
        // 特定类型的方法引用写法
        fruits.stream()
            .sorted(String::compareTo)  // compareTo是String的实例方法
            .forEach(s -> System.out.print(s + " "));
        
        // 另一个例子:获取字符串长度
        System.out.println("\n\n获取每个字符串的长度:");
        
        // Lambda表达式写法
        fruits.stream()
            .map(s -> s.length())
            .forEach(len -> System.out.print(len + " "));
        
        System.out.println("\n↓ 使用方法引用 ↓");
        
        // 特定类型的方法引用写法
        fruits.stream()
            .map(String::length)  // length()是String的实例方法
            .forEach(len -> System.out.print(len + " "));
        
        System.out.println("\n\n4. 构造器引用 (类名::new)");
        System.out.println("格式: ClassName::new\n");
        
        // 创建字符串列表
        System.out.println("创建对象列表:");
        
        List<String> stringValues = Arrays.asList("Java", "Python", "C++");
        
        // Lambda表达式写法:创建StringBuilder对象
        System.out.println("Lambda表达式创建StringBuilder:");
        List<StringBuilder> builders1 = stringValues.stream()
            .map(s -> new StringBuilder(s))
            .toList();
        builders1.forEach(sb -> System.out.print(sb + " "));
        
        System.out.println("\n↓ 使用构造器引用 ↓");
        
        // 构造器引用写法
        List<StringBuilder> builders2 = stringValues.stream()
            .map(StringBuilder::new)  // StringBuilder::new 是构造器引用
            .toList();
        builders2.forEach(sb -> System.out.print(sb + " "));
        
        // 数组的构造器引用
        System.out.println("\n\n数组构造器引用:");
        
        // Lambda表达式写法
        IntFunction<int[]> arrayCreator1 = size -> new int[size];
        
        // 构造器引用写法
        IntFunction<int[]> arrayCreator2 = int[]::new;
        
        int[] array = arrayCreator2.apply(5);
        System.out.println("创建了长度为 " + array.length + " 的数组");
    }
    
    // 静态方法,用于演示静态方法引用
    public static void printName(String name) {
        System.out.println("姓名: " + name);
    }
    
    // 内部类,用于演示实例方法引用
    static class PrefixProcessor {
        private String prefix;
        
        public PrefixProcessor(String prefix) {
            this.prefix = prefix;
        }
        
        // 实例方法
        public String addPrefix(String text) {
            return prefix + text;
        }
    }
}

3.11、常用API

3.11.1、String

java 复制代码
package Strings;
public class StringDemo {
  public static void main(String[] args) {
    // 1、创建字符串
    String str1 = "Hello World"; // 字面量创建,存储在常量池中
    String str2 = new String("Hello World"); // 使用new关键字创建,存储在堆内存中
    System.out.println("str1===str2: " + (str1 == str2)); // false
    System.out.println("str1.equals(str2): " + str1.equals(str2)); // true

    // 2、字符串不可变性
    String str3 = "Hello";
    str3 = str3 + " Java"; // 创建新字符串,str3指向新对象
    System.out.println("str3: " + str3); // Hello Java

    // 3、常用方法
    String text = "Hello World, Java Programming";
    System.out.println("长度: " + text.length()); // 29
    System.out.println("转大写: " + text.toUpperCase()); // HELLO WORLD, JAVA PROGRAMMING
    System.out.println("转小写: " + text.toLowerCase()); // hello world, java programming
    System.out.println("获取索引4的字符: " + text.charAt(4)); // o
    System.out.println("截取6-11: " + text.substring(6, 11)); // World
    System.out.println("从索引6开始截取: " + text.substring(6)); // World, Java Programming
    System.out.println("是否以'Hello'开头: " + text.startsWith("Hello")); // true
    System.out.println("是否以'ing'结尾: " + text.endsWith("ing")); // true
    System.out.println("是否包含'Java': " + text.contains("Java")); // true
    System.out.println("'Java'的位置: " + text.indexOf("Java")); // 13
    System.out.println("最后的'g'的位置': " + text.lastIndexOf("g")); // 28

    // 4、替换与分割
    System.out.println("替换'World'为'Universe': " + text.replace("World", "Universe")); // Hello Universe, Java
                                                                                      // Programming
    String csv = "苹果,香蕉,橙子,西瓜";
    String[] fruits = csv.split(",");
    for (String fruit : fruits) {
      System.out.println("分割后fruit: " + fruit);
    }
    System.out.println("去除首尾空格: '" + "  Hello World  ".trim() + "'"); // 'Hello World'

    // 5、字符串比较
    String a = "hello";
    String b = "HELLO";
    System.out.println("a.equals(b): " + a.equals(b)); // false
    System.out.println("a.equalsIgnoreCase(b): " + a.equalsIgnoreCase(b)); // true
    System.out.println("a.compareTo('hello'): " + a.compareTo("hello")); // 0
    System.out.println("a.compareTo('apple'): " + a.compareTo("apple")); // 7
    
    // 6、字符串连接
    String greet = "Hello";
    greet = greet.concat(" ").concat("World").concat("!");
    System.out.println("greet: " + greet); // Hello World!
    // 使用StringBuilder进行高效字符串操作
    StringBuilder sb = new StringBuilder();
    sb.append("Hello").append(" ").append("World");
    System.out.println("sb: " + sb.toString()); // Hello World
  }

}

3.11.2、String生成验证码

java 复制代码
package Strings;
public class Stringtest {
  public static void main(String[] args) {
    // 生成验证码
    String code = getCode(4);
    System.out.println(code);
  };
  public static String getCode(int n) {
    String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    String code = ""; // 记住验证码的随机字符
    //  循环n次,生成n个随机字符
    for (int i = 0; i < n; i++) {
      int number = (int) (Math.random() * str.length());
      code += str.charAt(number);
    }
    return code;
  }
}

3.11.3、ArrayList集合

java 复制代码
package Strings;
import java.util.*;
public class ArrayListDemo {
  public static void main(String[] args) {
    // 1、创建 ArrayList
    ArrayList<String> list = new ArrayList<String>();
    // 2、添加元素
    list.add("hello");
    list.add("world");
    list.add("java");
    System.out.println("初始列表" + list); // [hello, world, java]
    // 3、插入元素
    list.add(1, "android"); // 在索引1处插入android
    System.out.println("插入元素后列表:" + list); // [hello, android, world, java]
    // 4、获取元素
    String s = list.get(2);
    System.out.println("索引为2的元素是:" + s); // world
    // 5、修改元素
    list.set(0, "红苹果");
    System.out.println("修改元素后列表:" + list); // [红苹果, android, world, java]
    // 6、删除元素
    list.remove(1); // 删除索引1处的元素
    System.out.println("删除元素后列表:" + list); // [红苹果, world, java]
    list.remove("java"); // 删除元素java
    System.out.println("删除元素后列表:" + list); // [红苹果, world]
    // 7、大小和判空
    System.out.println("列表元素个数:" + list.size()); // 2
    System.out.println("列表是否为空:" + list.isEmpty()); // false
    // 8、遍历:for循环与迭代器
    for (int i = 0; i < list.size(); i++) {
      System.out.println("索引" + i + "的元素是:" + list.get(i));
    }
    for (String s1 : list) { // 增强for循环
      System.out.println("元素是:" + s1);
    }
    Iterator<String> it = list.iterator(); // 迭代器
    while (it.hasNext()) {
      System.out.println("迭代:" + it.next());
    }
    // 9、清空列表
    list.clear();
    System.out.println("清空列表后列表:" + list); // []
    System.out.println("列表大小:" + list.size()); // 0
  }
}

3.11.4、String与ArrayList区别

ArrayList:是可以装多个东西的袋子(动态数组),能增删改查,容量自动调整

String:是写好的文字(字符序列),一旦写好就不能修改,想要不同内容就得重新写

相关推荐
tkevinjd14 小时前
JUC4(生产者-消费者)
java·多线程·juc
sww_102614 小时前
Openfeign源码浅析
java·spring cloud
遇见~未来15 小时前
JavaScript构造函数与Class终极指南
开发语言·javascript·原型模式
foundbug99915 小时前
基于MATLAB的TDMP-LDPC译码器模型构建、仿真验证及定点实现
开发语言·matlab
X***078815 小时前
从语言演进到工程实践全面解析C++在现代软件开发中的设计思想性能优势与长期生命力
java·开发语言
smileNicky15 小时前
SpringBoot系列之集成Pulsar教程
java·spring boot·后端
毕设源码-钟学长15 小时前
【开题答辩全过程】以 基于Python的车辆管理系统为例,包含答辩的问题和答案
开发语言·python
Sammyyyyy15 小时前
Rust 1.92.0 发布:Never Type 进一步稳定
java·算法·rust
CCPC不拿奖不改名16 小时前
数据处理与分析:数据可视化的面试习题
开发语言·python·信息可视化·面试·职场和发展