面向对象

面向对象

1 面向对象思想概述

  • 面向过程开发,就是面向具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。

当需求单一,我们这样一步步操作是没问题的,并且效率高。但是随着需求的更改,功能的增多,发现需要面对每一个步骤比较麻烦。

能不能把这些步骤和功能进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起,这就是面向对象思想。

  • 面向对象思想
    • 面向对象思想是基于面向过程编程思想
    • 面向过程强调的是每一个功能的步骤,代表语言是C语言
    • 面向对象强调的是对象,然后由对象去调用功能
  • 特点:
    • 更符合我们思考习惯的思想
    • 可以将复杂的事情简单化
    • 我们从执行者变成了指挥者

2 类和对象及其使用

我们如何表示现实世界的事物?

  • 属性
    • 就是该事物的描述信息
  • 行为
    • 就是该事物能够做什么

学习java语言最基本的单位是类,所以我们应该把事物用类来体现。

类是一组相关的属性和行为的集合。

对象:是该类事物的具体体现。

类的定义:

  • 属性(成员变量):和以前定义变量一样,只不过位置放到类中方法外。
  • 行为(成员方法):和以前定义方法一样,只不过把static去掉。
public class Student {
    // 属性 成员变量
    /**
     * 学号
     */
    int id;
    /**
     * 姓名
     */
    String name;
    /**
     * 年龄
     */
    int age;


    // 行为  成员方法
    public void study(){
        System.out.println("好好学习,天天向上");
    }

    public void playGame(String gameName){
        System.out.println("打" + gameName + "游戏");
    }
}
  • 对象的创建及其使用

    • 创建对象:

    • 类名 对象名 = new 类名(参数);
      
    • 使用对象访问类中的成员

      • 对象名.成员变量
      • 对象名.成员方法
public static void main(String[] args) {
        // 创建对象
        Student student = new Student();
        System.out.println(student.id);
        System.out.println(student.name);
        System.out.println(student.age);
        student.id = 1;
        student.name = "小谷";
        student.age = 20;
        student.study();
        student.playGame("原神");
        System.out.println(student.id);
        System.out.println(student.name);
        System.out.println(student.age);

    }

3 对象的内存图

一个对象的内存图
image-20250625153256066

两个对象的内存图
image-20250625153924003

4 成员变量和局部变量的区别

  • 内存的位置不同:

    • 成员变量在堆中

    • 局部变量在栈中

  • 初始化值不同

    • 成员变量有默认值
    • 局部变量没有默认值,必须先定义赋值才能使用
  • 类中的位置不同

    • 成员变量是在类中方法外
    • 局部变量在方法中或者方法的声明上(形式参数)
  • 生命周期不同

    • 成员变量:随着对象的创建而存在,随着对象的消失而消失
    • 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失

5 构造方法

作用: 给对象的数据进行初始化

格式:

  • 方法名和类名相同
  • 没有返回值类型,连void都没有
  • 没有具体的返回值
修饰符 构造方法名(参数列表){

}

注意:如果不提供构造方法,系统会默认给出一个无参的构造方法

构造方法是可以重载的

如果提供了任何的构造方法,那么默认的无参构造方法就不会提供了

public Student(){

    }

    public Student(int i,String n,int a){
        id = i;
        name = n;
        age = a;
    }

6 private

private是一个权限修饰符,可以修饰成员,被private修饰的成员只能在本类中才能访问。

private最常见的应用:

  • 把成员变量用private修饰
  • 提供对应的setXxx()和getXxx()方法
// 属性 成员变量
    /**
     * 学号
     */
    private int id;
    /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private int age;

    // 提供公共的set方法进行赋值
    public void setId(int i){
        id = i;
    }

    public void setAge(int a){
        if (a < 0 || a > 120){
            age = 18;
            return;
        }
        age = a;
    }

    // 提供公共的get方法获取属性值
    public int getId(){
        return id;
    }

    public int getAge(){
        return age;
    }

    public void setName(String n){
        name = n;
    }

封装的思想:

  • 面向对象三大特征之一(封装、继承、多态)
  • 原则:将不需要对外提供的内容都隐藏起来
  • 把属性隐藏,提供公共的方法对其访问

好处:

  • 通过方法来控制成员变量的操作,提供了代码的安全性
  • 把代码用方法进行封装,提高了代码的复用性

7 this

this代表对象,谁调用就是哪个对象

  • this.成员变量 获取对象的成员变量
  • this.成员方法 调用本类对象的成员方法
  • this(参数) 调用本类的构造方法
    • this(参数) 必须在构造方法体中有效代码的第一行
public Person(){
//        int a = 10;
        this("张三","男","zhangsan@qq.com");
    }

    public Person(String name,String gender,String email){
        this(name,gender);
        this.email = email;
    }

8 构造代码块

{
        // 构造代码块  会在每一个构造方法执行之前执行
        this.eat();
        this.cry();
    }
public static void main(String[] args) {
        Baby baby = new Baby(7.0);
        {// 局部代码块  提高栈内存的利用率
            int a = 10;
        }
        System.out.println(a);
    }

练习:定义一个类表示矩形,提供求周长和面积的方法

package cn.javasm.demo;

/**
 * @className: TestDemo6
 * @description:
 * @author: gfs
 * @date: 2025/6/25 17:40
 * @version: 0.1
 * @since: jdk17
 */
public class TestDemo6 {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle(3.0,4.0);
        System.out.println(rectangle.getArea());
        System.out.println(rectangle.getGirth());
    }
}

// 矩形
class Rectangle{

    // 属性
    /**
     * 长
     */
    private double length;
    /**
     * 宽
     */
    private double width;

    public Rectangle(double length,double width){
        if (length > 0 && width > 0){
            this.length = length;
            this.width = width;
        }

    }


    public double getWidth(){
        return width;
    }

    public double getLength(){
        return length;
    }

    // 行为
    public double getGirth(){
        return 2 * (length + width);
    }

    public double getArea(){
        return length * width;
    }

}

9 继承

如果一些类中的属性和方法是重复的,可以将这些重复的属性和方法.提取到一个新的类中,利用extends关键字让原来的类和新的类产生联系,这种联系称之为继承。提取出来的新的类是父类(超类/基类),原来的类是子类(派生类)。子类通过继承父类可以使用父类中的一部分方法和属性。

注意:子类通过继承可以继承父类全部的属性和方法,但是只有一部分的属性和方法对子类可见。

优点:提高代码的复用性

注意:java中只有单继承,没有多继承。但是有多层继承。

继承关系中的顶级父类是Object,任何类都是Object类的子类

单继承和多继承的比较:多继承在代码的复用性上要优于单继承,但是多继承在调用方法时会产生歧义.

class Pet{
     String name;

    String kind;

    String color;

    public void eat(){
        System.out.println("吃");
    }
}
class Animal{
    public int eat(){
        return 1;
    }
}
class Cat extends Pet{

    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

class Dog extends Pet{

}

10 权限修饰符

本类中 子类中 同包类中 其他类中
public 可以 可以 可以 可以
protected 可以 可以 可以 不可以
默认 可以 同包子类可以 可以 不可以
private 可以 不可以 不可以 不可以

11 重写

在父子类中存在了方法签名相同的非静态方法,也称之为是方法的覆盖

// 代表职业的类
class Proffession{
    public void work(){
        System.out.println("在工作中...");
    }
}

class Doctor extends Proffession{
    public void work(){
        System.out.println("治病救人,悬壶济世");
    }
}

重写的"两等两小一大"原则:

  • 方法签名一致
  • 如果父类中的方法的返回值类型是基本数据类型/void,那么子类在重写方法的时候返回值类型要和父类保持一致
  • 子类重写的方法的权限修饰符的范围要大于等于父类对应方法的权限修饰符的范围
  • 如果父类方法的返回值类型是引用数据类型,那么子类重写的方法的返回值类型要么与父类方法返回值类型一致,要么子类方法的返回值类型是父类方法返回值类型的子类
class Proffession{

    protected A work(){
        System.out.println("在工作中...");
        return null;
    }
}

class Doctor extends Proffession{
    @Override// 检查是否符合重写的语法
    public B work(){
        System.out.println("治病救人,悬壶济世");
        return null;
    }
}

class A{}
class B extends A{}
  • 异常那天再讲

12 super

super代表父类对象

用法:

  • super.成员方法(参数)
    • 调用父类的成员方法
  • super(参数)
    • 调用父类的构造方法
    • 必须在构造方法体的有效代码第一行
  • super.成员变量
    • 调用父类的成员变量

13 多态

封装、继承、多态是面向对象的三大特性

多态的体现形式:

  • 编译时多态:方法的重载
  • 运行时多态:方法的重写、向上转型

向上转型:

  • 用父类来声明对象,用子类来创建对象

  • 格式:  父类 对象名 = new 子类();
    
    public static void main(String[] args) {
        // 父类 对象名 = new 子类();
        // 在使用向上转型创建对象的时候,编译期间只会检查声明类和创建类之间是否有继承关系
        // 编译期间并不确定到底是哪一个子类
        // 到了运行期间才会确定具体的子类

        // 编译看左边,运行看右边
        Employee employee = new Developer();
        employee.work();
        Employee employee1 = new Tea();

//        addSalary(new Developer());

        Employee employee2 = addSalary(new Tea());
        Tea tea1 = (Tea) employee2;
        System.out.println(tea1.email);

    }

    public static Employee addSalary(Employee employee){
        // 向下转型  强转
        Tea tea = (Tea) employee;
        System.out.println(tea.email);
        return new Tea();
    }
}


class Employee{

    public void work(){
        System.out.println("工作");
    }
}

class Developer extends Employee{

    String name;
    public void work(){
        System.out.println("write the code,change the world");
    }
}

多态可以解耦

高内聚低耦合

思考:为什么子类重写的方法的权限修饰符的范围要大于等于父类对应方法的权限修饰符的范围?

class A{
    public void m(){}
}

class B extends A{
    private void m(){}
}

A a = new B();// a对象的声明类是A类,所以a对象能够干什么看的是A类,A类告诉a对象有一个m方法,可以在任何地方使用
a.m();// a对象的实际类型是B类创建的,所以运行起来后看的是B类,B类中的m方法不能在其他类中访问
  • 为什么如果父类方法的返回值类型是引用数据类型,那么子类重写的方法的返回值类型要么与父类方法返回值类型一致,要么子类方法的返回值类型是父类方法返回值类型的子类?
class A{}

class B extends A{
	public void mb(){}
}

class C{
    public B m(){}
}

class D extends C{
    public A m(){}
}

C c = new D();// c对象的声明类型是C类型,C类型告诉c对象有一个m方法可以使用,并且返回值是B类
B b = c.m();//c对象的实际类型是D类型,所以m方法的执行看的是D类,D类告诉c对象m方法返回的是A类--子类接收一个父类对象
b.mb();// b对象真正的类型是A类,A类根本就没有mb方法

14 static

static是一个修饰符,可以修饰数据、方法、代码块、内部类

14.1 静态变量

static修饰的变量称之为静态变量/类变量。静态变量是随着类的加载而加载到方法区,在方法区中被赋予了默认值。静态变量是先于对象而存在的,可以通过类名来调用。该类所产生的所有的对象共用的是同一个静态变量。每一个对象存储的是该静态变量的地址。静态变量是属于类的,不是属于对象的,所以使用类名来调用。

image-20250627103311783

类在第一次被使用到的时候会加载到方法区

类只加载一次

课堂练习:统计一个类创建对象的个数

image-20250627110904525

14.2 静态方法

被static修饰的方法称之为静态方法。静态方法在类加载的时候加载到方法区,并没有执行只是存储在方法区。在方法被调用的时候到栈内存中执行。静态方法本身也是先于对象存在,所以习惯上通过类名来调用静态方法。

  • 静态方法中不能使用this/super
  • 静态方法中不能调用本类的非静态方法
  • 静态方法可以被继承
  • 静态方法不可以重写,父子类中存在方法签名一致的静态方法,称之为隐藏(hide).隐藏也适用于重写的五个原则
  • 静态方法中不可以访问成员变量
        A a = new B();
        a.m();
    }
}


class A{
    public static void m(){
        System.out.println("A.........");
    }
}

class B extends A{

    public static void m(){
        System.out.println("b......");
    }
}

14.3 静态代码块

用static修饰的代码块就是静态代码块,在类加载的时候执行一次。静态代码块在栈内存中执行

static {
        // 静态代码块
        // 会在类加载的时候执行一次
        System.out.println("run......");
    }

执行顺序问题

A1 A2 A3

public class TestDemo2 {
    public static void main(String[] args) {
        new SA();
    }
}


class SA{
    static {
        System.out.println("A1");
    }

    {
        System.out.println("A2");
    }

    public SA(){
        System.out.println("A3");
    }
}

A1 B1 A2 A3 B2 B3

public class TestDemo2 {
    public static void main(String[] args) {
        new SB();
    }
}


class SA{
    static {
        System.out.println("A1");
    }

    {
        System.out.println("A2");
    }

    public SA(){
        System.out.println("A3");
    }
}

class SB extends SA{
    static {
        System.out.println("B1");
    }

    {
        System.out.println("B2");
    }

    public SB(){
        System.out.println("B3");
    }
}

A1 C B1 A2 A3 B2 B3

public class TestDemo2 {
    public static void main(String[] args) {
        new SB();
    }
}


class SA{
    static {
        System.out.println("A1");
    }

    {
        System.out.println("A2");
    }

    public SA(){
        System.out.println("A3");
    }
}

class SB extends SA{
    static SC c = new SC();
    static {
        System.out.println("B1");
    }

    {
        System.out.println("B2");
    }

    public SB(){
        System.out.println("B3");
    }
}

class SC{
    public SC(){
        System.out.println("C");
    }
}

A1 C B1 A2 A3 B2 B3

public class TestDemo2 {
    public static void main(String[] args) {
        new SB();
    }
}


class SA{

    SD d;

    static {
        System.out.println("A1");
    }

    {
        System.out.println("A2");
    }

    public SA(){
        System.out.println("A3");
    }
}

class SB extends SA{
    static SC c = new SC();
    static {
        System.out.println("B1");
    }

    {
        System.out.println("B2");
    }

    public SB(){
        System.out.println("B3");
    }
}

class SC{
    public SC(){
        System.out.println("C");
    }
}


class SD extends SC{
    public SD(){
        System.out.println("D");
    }
}

A1 C B1 A2 C D A3 B2 B3

public class TestDemo2 {
    public static void main(String[] args) {
        new SB();
    }
}


class SA{

    SD d;

    static {
        System.out.println("A1");
    }

    {
        System.out.println("A2");
        d = new SD();
    }

    public SA(){
        System.out.println("A3");
    }
}

class SB extends SA{
    static SC c = new SC();
    static {
        System.out.println("B1");
    }

    {
        System.out.println("B2");
    }

    public SB(){
        System.out.println("B3");
    }
}

class SC{
    public SC(){
        System.out.println("C");
    }
}


class SD extends SC{
    public SD(){
        System.out.println("D");
    }
}

总结:父类静态-->子类静态-->父类非静态-->子类非静态

static面试题补充

java文件经过编译成为class文件,class文件加载到内存中

class文件在加载到内存的过程中分为:

  • 加载-- 将class文件转化为二进制的字节码
  • 校验-- 检查字节码的安全性
  • 准备-- 将静态变量放到方法区并分配空间,然后标记一个默认值,标记的值可能会被舍弃
  • 解析-- 常量符号引用的替换
  • 初始化--执行静态变量的赋值以及静态代码块,没有特定的先后顺序,代码谁在上先执行谁
static int i = 3;
static{
  i = 5;
}
// 结果是5
// 准备阶段:先静态变量i放入方法区,并且标记默认值为0.
// 初始化阶段:先执行静态变量的赋值,舍弃标记值0,把3赋值给i。然后执行静态代码块,将i的值由3改成5
static{
  i = 5;
}

static int i = 3;

// 结果是3
// 准备阶段:先将静态变量i放入方法区,并且标记默认值0
// 初始化阶段:先执行静态代码块,将i的标记值由0改成5,然后执行i的赋值,发现有初始值3,所以舍弃标记值5,将3作为最终值设置进去
static {
   i = 5;
}
static int i;
// 结果是5
// 准备阶段:先将静态变量i放入方法区,标记默认值0
// 初始化阶段:先执行静态代码块,将i的标记值由0改成5,然后执行i的赋值,发现i没有初始化值,那么将标记值5设置为最终值

注意:静态变量在没有执行赋值语句(标记值状态),不允许直接操作

class D{
    static {
        i += 5;
    }
    static int i = 3;
}
// 编译失败
public class TestDemo3 {
    public static void main(String[] args) {
//        System.out.println(D.i);
        System.out.println(SDemo.i);
        System.out.println(SDemo.j);

        System.out.println(SE.i);
        System.out.println(SE.j);
    }
}

class D{
//    static int i;
//    static {
//        i += 5;
//    }
}

class SDemo{
    // 准备阶段:加载sd标记值为null,i标记值为0,j标记值为0
    // 初始化阶段:先执行sd=new SDemo();执行构造方法,会将i的标记值变为1,
    // j的标记值变为1,再执行i = 5,舍弃标记值1,将5赋值给i,j没有初始值,把标记值1作为最终值赋值
    static SDemo sd = new SDemo();
    static int i = 5;
    static int j;
    public SDemo(){
        i++;
        j++;
    }
}

class SE{
    // SE.i属于引用操作
    static {
        SE.i++;
        SE.j++;
    }
    // 结果是5 1
    static int i = 5;
    static int j;
}

15 final

final是一个修饰符,修饰数据、方法、类

15.1 常量

final修饰的数据称之为常量,定义之后不能更改

final修饰的基本数据类型,是其值不能更改,final修饰的引用数据类型,是地址不能更改,其中的值或者元素可以更改

public class TestDemo4 {
    public static void main(String[] args) {
        final int I;
        I = 8;
        add(I);

        // arr是对象,地址不能改变
        final int[] arr = {1,2,3,4,5};
        arr[2] = 10;
//        changeRef(arr);
//        arr = new int[3];
        // 扩容后地址发生改变
//        arr = Arrays.copyOf(arr,arr.length * 2);

    }

//    public static void changeRef(final int[] arr){
//        arr = Arrays.copyOf(arr,arr.length * 2);
//    }

    public static void add(int i){
        i++;
    }
}
public class Person {
    // final修饰的成员变量,要求在对象创建完成之前赋值
    final int i;

    // 静态常量,要求在类加载完成之前赋值
    final static int j;
    static {
        j = 20;
    }

常量的命名是全部大写,多个单词之间用_隔开

15.2 最终方法

final修饰的方法称之为最终方法--不能被重写/隐藏

final修饰的方法可以被继承

class Demo{
    public  final void eat(){
        System.out.println("吃火锅");
    }
    
    public void eat(int a){
        
    }
}

class DA extends Demo{
//    public  void eat(){
//
//    }
}

15.3 最终类

final修饰的类称之为最终类,不能被继承

16 abstract

被abstract修饰的方法称之为抽象方法,抽象方法没有方法体,一定要在抽象类或者接口中

抽象类必须要被继承,所以不能使用final修饰

抽象类不能被实例化,要被继承后在子类中重写抽象方法

抽象类中可以定义构造方法,成员变量,成员方法,静态变量,静态方法

抽象方法可以重载,必须要被继承和重写,不能用private/final/static

抽象类也是类,也受到java单继承的限制,使用的不多

package cn.javasm.demo;

/**
 * @className: TestDemo5
 * @description:
 * @author: hlx
 * @date: 2025/6/27 15:55
 * @version: 0.1
 * @since: jdk17
 */
public class TestDemo5 {
    public static void main(String[] args) {
        // 抽象类不能直接实例化
//        Proffession proffession = new Proffession();
//        proffession.work();
        // 使用多态创建对象
//        Proffession proffession = new Developer();
//        proffession.work();

        Proffession.eat();
    }
}


// 抽象类
abstract class Proffession{
    // 抽象方法 没有方法体 需要被abstract
    // 抽象方法必须在抽象类或者接口中
    public  abstract void work();

    public abstract void work(int a);

    private  int age;

    private String name;

    public static void eat(){
        System.out.println("吃工作餐");
    }

    public Proffession(){

    }
}

abstract class Doctor extends Proffession{
    @Override
    public void work() {

    }
}

class Developer extends Proffession{
    @Override
    public void work() {
        System.out.println("程序员要敲代码");
    }

    @Override
    public void work(int a) {

    }
}

class Teacher extends Proffession{
    @Override
    public void work() {
        System.out.println("老师教书育人");
    }

    @Override
    public void work(int a) {

    }
}


abstract class Police extends Proffession{
    @Override
    public void work() {
        System.out.println("防诈骗");
    }

}

class FuJing extends Police{

    @Override
    public void work(int a) {

    }
}

17 interface

接口是功能的集合,同样看作一种引用数据类型

在jdk1.8之前,接口只描述所应该具备的方法,并没有具体的实现。具体的实现由接口的实现类来完成。这样将功能的定义与实现分离,优化了程序设计。

接口中没有构造方法

用interface定义接口,用implements让类实现接口。类在实现接口之后,要实现接口中的所有的抽象方法

接口不能实例化,也没有构造方法。接口在编译完成之后会产生class文件,但它不是类

在java中接口和接口之间是多继承

interface ProductDao extends UserDao,Proffession{
    
}

类和接口之间是多实现

// 实现类中实现接口所有的抽象方法
class Actor implements Proffession,UserDao{


    @Override
    public void work() {
        System.out.println("演习");
    }

    @Override
    public double salary() {
        return 11000;
    }

    @Override
    public void addUser() {
        System.out.println("添加新演员");
    }

    @Override
    public void queryUser() {
        System.out.println("查询演员");
    }
}

课堂练习:定义一个接口表示形状(shape),提供获取这个形状的周长(girth)和面积(area),为这个接口提供实现类:矩形(rectangle)圆形(circle)和正方形(square)

public class TestDemo2 {
    public static void main(String[] args) {
        Shape shape = new Square(10);
        System.out.println(shape.getGirth());
        System.out.println(shape.getArea());
    }
}

// 定义一个接口表示形状(shape),提供获取这个形状的周长(girth)和面积(area),
// 为这个接口提供实现类:矩形(rectangle)圆形(circle)和正方形(square)
interface Shape{
    // 获取周长
    double getGirth();

    // 获取面积
    double getArea();
}

// 定义实现类圆形实现形状接口
class Circle implements Shape{
    // 半径
    private double radius;

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

    public double getRadius() {
        return radius;
    }

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

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

// 矩形
class Rectangle implements Shape{

    // 长
    private double length;

    // 宽
    private double width;

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

    public double getLength() {
        return length;
    }

    public double getWidth() {
        return width;
    }

    @Override
    public double getGirth() {
        return 2 * (length + width);
    }

    @Override
    public double getArea() {
        return length * width;
    }
}

// 正方形
class Square extends Rectangle{
    public Square(double width){
        super(width,width);
    }
}

JDK8后接口的特性

接口中的实体方法

package cn.javasm.demo;

/**
 * @className: MyInter
 * @description:
 * @author: hlx
 * @date: 2025/6/30 10:02
 * @version: 0.1
 * @since: jdk17
 */
@FunctionalInterface // 函数式接口  这个注解要求接口中只有一个抽象方法
public interface MyInter {
    void update();

    /*
        jdk8中可以使用default修饰实体方法
     */
    default boolean defaultMethod(){
        privateMethod();
        return true;
    }

    /**
     * JDK8之后可以使用static修饰实体方法
     */
    static void method(){
        privateStaticMethod();
        System.out.println("static方法");
    }

    // jdk9之后可以定义私有的实体方法
    private void privateMethod(){
        System.out.println("私有的实体方法");
    }

    // jdk9之后可以定义私有的静态方法
    private static void privateStaticMethod(){
        System.out.println("私有的静态方法");
    }
}

lambda表达式

lambda表达式是JDK1.8之后的特性,要使用这个表达式,要求接口中只能有一个抽象方法

lambda表达式本质上是在替换匿名内部类的语法,让程序员编写代码更加简单

缺点:可读性比较差

lambda表达式是面向函数式 编程,面向接口中的那一个唯一的抽象方法编程

基本语法:

(参数) -> {方法体}

无参无返回值

public interface MyInterface {
    // 无参无返回值
    void hello();
}
public class TestDemo2 {
    public static void main(String[] args) {
//        MyInterface myInterface = new MyInterfaceImpl();
        // 匿名内部类
//        MyInterface myInterface = new MyInterface(){
//            @Override
//            public void hello() {
//                System.out.println("hello shangma");
//            }
//        };

        // lambda表达式
//        MyInterface myInterface = ()->{
//            System.out.println("hello shangma");
//        };
        // 如果方法体中的代码只有一行,可以省略{}
//        MyInterface myInterface = () -> System.out.println("hello shangma");
//        myInterface.hello();

        MyInterface myInterface1 = test(()-> System.out.println("hello..."));
        myInterface1.hello();
    }

    public static MyInterface test(MyInterface myInterface){
        myInterface.hello();
        return () -> {
            System.out.println("haha");
            System.out.println("xixi");
        };
    }
}


class MyInterfaceImpl implements MyInterface{

    @Override
    public void hello() {
        System.out.println("hello world");
    }
}

有参无返回值

public interface MyInterface {
    // 有参无返回值 参数只有一个
//    void compareStr(String str);
    // 参数有多个
    void max(int num1,int num2);
}

    public static void main(String[] args) {
        // 匿名内部类
//        MyInterface myInterface = new MyInterface(){
//            @Override
//            public void compareStr(String str) {
//                System.out.println("hello".equals(str));
//            }
//        };

        // lambda表达式
//        MyInterface myInterface = (String str) -> System.out.println("hello".equals(str));
        // 参数的参数类型可以省略
//        MyInterface myInterface = (str) -> System.out.println("hello".equals(str));
        // 参数如果只有一个,可以省略()
//        MyInterface myInterface = a -> System.out.println("hello".equals(a));
//        myInterface.compareStr("hello");

        // 参数有多个
        // lambda表达式
//        MyInterface myInterface = (int num1,int num2) -> System.out.println(num1 > num2 ? num1 : num2);
//        MyInterface myInterface = (num1,num2) -> System.out.println(num1 > num2 ? num1 : num2);
        MyInterface myInterface = (a,b) -> System.out.println(a > b ? a : b);
        myInterface.max(10,20);
    }

有参有返回值

    //有参有返回值
//    int abc(int num1,int num2);

//    boolean compareStr(String str);

    // 获取用户对象的一个属性
    Object getPropertyValue(User user);




    public static void main(String[] args) {
        // 匿名内部类
//        MyInterface myInterface = new MyInterface() {
//            @Override
//            public int max(int num1, int num2) {
//                return num1 > num2 ? num1 : num2;
//            }
//        };

        // lambda表达式
//        MyInterface myInterface = (a,b) -> {
//            return a > b ? a : b;
//        };
        // 如果{}中只有一行返回语句的代码,那么可以省略{}和return
//        MyInterface myInterface = (a,b) -> a > b ? a : b;
        // 如果方法体中的代码是已经有的静态方法,并且重写的方法的形参数量和类型以及返回值类型 和抽象方法一样,那么就可以使用类名::语法
//        MyInterface myInterface = Math::max;
//        System.out.println(myInterface.abc(20, 10));

        // 匿名内部类
//        MyInterface myInterface = new MyInterface() {
//            @Override
//            public boolean compareStr(String str) {
//                return "hello".equals(str);
//            }
//        };
        // lambda表达式
//        MyInterface myInterface = (s) -> "hello".equals(s);
//        MyInterface myInterface = "hello"::equals;
//        System.out.println(myInterface.compareStr("world"));

        // 匿名内部类
//        MyInterface myInterface = new MyInterface() {
//            @Override
//            public Object getPropertyValue(User user) {
//                return user.getName();
//            }
//        };

        // lambda表达式
//        MyInterface myInterface = u -> u.getName();
        MyInterface myInterface = User::getName;
        User user = new User();
        user.setName("张无忌");
        user.setAge(20);
        System.out.println(myInterface.getPropertyValue(user));
    }

18 类型转换问题

public class TestDemo3 {
    public static void main(String[] args) {
        // 在java中,类和类之间支持的是单继承
        // 因此可以形成一颗继承结构树
        // 所以比较容易确定两个类之间是否有继承关系
        // 在编译期间会检查对象的声明类型和要强转的类型是否有继承关系
        A a = new B1();
        // a对象的声明类型是A类,a对象要强转的类型是B1,B1和A类有继承关系,所以编译通过
        // 到了运行的时候,发现a对象的实际类型是B1,要强转的类型也是B1,类型匹配,强转成功
//        B1 b1 = (B1)a;
        // a对象的声明类型是A类,a对象要强转的类型是B2,B2和A类有继承关系,所以编译通过
        // 到了运行的时候,发现a对象的实际类型是B1,要强转的类型也是B2,类型不匹配ClassCastException,强转失败
//        B2 b2 = (B2)a;
        // a对象的声明类型是A类,a对象要强转的类型是C,C和A类没有继承关系,所以编译不通过
//        C c = (C)a;
        // 在java中,接口和接口之间是多继承,类和接口之间是多实现
        // 所以形成了一张网状结构
        // 在网状结构中,不容易确定两个节点之间的关系
        // 所以为了提高编译效率,java在编译期间放弃检查
        // 也就意味着在编译期间,任何一个接口都可以强转
        D d = (D)a;
    }
}

class A{}
class B1 extends A{}
class B2 extends A{}
class C{}
interface D{}

19 内部类

  • 匿名内部类
  • 局部内部类
  • 成员内部类
  • 静态内部类

19.1 匿名内部类

package cn.javasm.demo;

/**
 * @className: TestDemo
 * @description:
 * @author: hlx
 * @date: 2025/6/30 10:02
 * @version: 0.1
 * @since: jdk17
 */
public class TestDemo {

    public static void main(String[] args) {
        // 接口 = 实现类
        MyInter myInter = new MyInterImpl();
        test(new MyInter() {
            @Override
            public void update() {
                System.out.println("更新");
            }
        });
//        MyInter myInter1 = new MyInter(){ // 匿名内部类
//            @Override
//            public void update() {
//                System.out.println("匿名内部类更新了");
//            }
//        };
//
//        myInter.update();
//
//
//        Profession profession = new Profession(){// 匿名内部类
//            @Override
//            public void add() {
//
//            }
//        };
//
//        // 只要是接口都可以使用匿名内部类,只要一个类能够被继承就可以使用匿名内部类
//        // 匿名内部类本质上就是接口的实现类或者类的子类
//
//        Doctor doctor = new Doctor(){// Doctor的子类
//            @Override
//            public void add() {
//                System.out.println("添加手术刀");
//            }
//        };
//
//        doctor.add();

//        Teacher teacher = new Teacher(){};
        
        
    }
    
    public static MyInter test(MyInter myInter){
        
        return new MyInter(){
            @Override
            public void update() {
                
            }
        };
    }
}

final class Teacher{

}

abstract class Profession{
    public abstract void add();
}

class Doctor extends Profession{

    @Override
    public void add() {
        System.out.println("添加医生");
    }
}

image-20250630113731086

19.2 局部内部类[了解]

class Outer1{
    int i = 5;
    static int j = 10;
    public void test(){
        System.out.println("test..");
    }
    public static void test2(){
        System.out.println("test2");
    }
    public void m(){
        System.out.println("Outer1...");
        // 局部内部类/方法内部类
        // 只能在定义它的方法中使用
        // 可以使用外部类中的一切属性和方法
        // 只能被abstract/final中的一个修饰
        class Inner1{
            // 如果内部类和外部类有相同的变量,那么采用就近原则
            int i = 100;
            static  int x = 10;
            public void m(){
                System.out.println(i);
                System.out.println(j);
                System.out.println("Inner1...");
                test();
                test2();
                System.out.println(x);
                test3();
                // 使用外部类的静态变量
                System.out.println(Outer1.j);
                // 使用外部类的成员变量
                System.out.println(Outer1.this.i);
            }

            public static void test3(){
                System.out.println("test3...");
            }

        }

        Inner1 inner1 = new Inner1();
        inner1.m();
    }
}

19.2 成员内部类[了解]

public class TestDemo {
    public static void main(String[] args) {
        // 使用成员内部类
        Outer2.Inner2 oi2 = new Outer2().new Inner2();
        oi2.m();
    }
}

class Outer2{
    int i = 8;
    static int j = 10;
    public void test(){
        System.out.println("test");
    }
    public static void test2(){
        System.out.println("test2");
    }
    // 成员内部类
    // 可以使用外部类中的一切属性和方法
    // 可以定义一切的属性和方法
    // 可以使用一切的修饰类的修饰符 比如public private abstract等等
    class Inner2{
        int i = 10;
        static int x = 101;
        public void m(){
            System.out.println(i);
            System.out.println(j);
            test();
            test2();
            test3();
            System.out.println(x);
        }

        public static void test3(){
            System.out.println("test3");
        }

    }
}

19.3 静态内部类[了解]

public class TestDemo {
    public static void main(String[] args) {
        // 使用静态内部类
        Outer3.Inner3 oi3 = new Outer3.Inner3();
        oi3.m();
    }
}

class Outer3{
    // 一个类如果能被static修饰,那么一定是内部类
    static int i = 3;
    int j = 10;
    public static void m2(){

    }
    // 静态内部类
    // 能够使用外部类的静态属性和静态方法
    // 不能使用外部类的非静态属性和非静态方法
    // 可以定义一切的属性和方法
    static class Inner3{
        int x = 100;
        static int y = 200;
        public void m(){
            System.out.println(i);
            m2();
        }

        public static void m3(){
            System.out.println("m3");
        }
    }
}

20 枚举

一个类产生的对象是固定的数量,并且不允许别人修改,就称之为叫枚举

枚举一般都会结合switch语句使用

20.1 JDK5之前的枚举类

class Season{
    // Season对象只能创建4个对象

    // 私有的构造方法 保证其他类不能随便创建对象
    private Season(){}

    public final static Season spring = new Season();
    public final static Season summer = new Season();
    public final static Season autumn = new Season();
    public final static Season winter = new Season();
    
}

20.2 JDK5之后的枚举类

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

        Season spring = Season.SUMMER;
        switch (spring){
            case SPRING:
                System.out.println("春天");
                int month = spring.getMonth();
                System.out.println(month);
                break;
            case SUMMER:
                System.out.println("夏天");
                spring.play();
                break;
            case AUTUMN:
                System.out.println("秋天");
                break;
            case WINTER:
                System.out.println("冬天");
                break;
        }



    }
}

enum AxiosStatus{
    OK(20000,"成功"),
    ERROR(50000,"失败"),
    PHONE_NOT_FOUND(30001,"手机号找不到"),
    EMAIL_NOT_FOUND(30002,"邮箱未找到");
    private int code;

    private String message;

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    AxiosStatus(int code, String message) {
        this.code = code;
        this.message = message;
    }
}

// 枚举类 它是一个类,可以定义属性和方法
enum Season{
    // 枚举中的常量都是被 public static final修饰
    // 枚举常量要求在枚举类的有效代码的第一行
    // public final static Season SPRING = new Season(3);
    SPRING(3){// 匿名内部类
        @Override
        public void play() {
            System.out.println("放风筝");
        }
    },
    SUMMER(7){
        @Override
        public void play() {
            System.out.println("游泳");
        }
    },
    AUTUMN(10){
        @Override
        public void play() {
            System.out.println("登山");
        }
    },
    WINTER(1){
        @Override
        public void play() {
            System.out.println("打雪仗");
        }
    };

    private int month;
    // 枚举中的成员变量一般不会提供set方法
    public int getMonth() {
        return month;
    }

    // 构造方法被private修饰
    Season(int month){
        this.month = month;
    }

    public abstract void play();
}
posted @ 2025-07-04 22:57  小胡coding  阅读(10)  评论(0)    收藏  举报