Java面向对象一篇通:从类与对象到Lambda(万字详解)

目录

1. 类(Class)

2. 对象(Object)

3. 类与对象的关系

4. 类的基本语法

1. 构造器(Constructor)

2. this 关键字

3. 封装(Encapsulation)

4. Javabean

5. static 关键字

总结

5.final

6.继承

1.认识继承

2.权限修饰符

3.继承的特点​编辑

1.单继承

2.就近原则

4.方法重写

5.子类构造器的特点

7.多态

1.认识多态

2.多态的好处

3.多态下的类型转换

8.单例类

1.饿汉式单例类​编辑

2.懒汉式单例类

9.枚举类

10.抽象类

11.接口

注意事项

13.代码块

14.内部类

1.成员内部类

2.静态内部类

3.局部内部类

15.函数式编程

16.lambada表达式

1. 从匿名内部类到Lambda的初始转换

2. 参数类型的省略

3. 单参数的括号省略

4. 单行代码的大括号和分号省略

5. 多参数但无操作的参数忽略

6. 方法引用的进一步简化

简化规则总结表

简化流程示例

关键原理


要想了解面向对象那么我们就要先了解一下去对象那么:什么是对象呢?

🔑现实中的明星就可以看到做对象,她代表一种特殊的类型

面向对象编程(Object-Oriented Programming, OOP)是一种以对象为核心的编程范式。它将现实世界中的事物抽象成程序中的“对象”,每个对象都包含状态和行为,从而使程序结构更清晰、更易于管理和扩展。

在 Java 中,对象是类的一个具体实例。可以把类看作是模板或蓝图,而对象则是根据这个模板创建出的实际“实体”。每个对象都具有:

  • 状态(属性):对象的特性,例如一个 Star 对象可能有姓名和年龄。

  • 行为(方法):对象能执行的操作,比如 speak() 方法。

对象通过 new 关键字创建,并在内存中分配空间,从而能够独立地保存状态和执行行为。 PS: 类的命名规则也会有讲究的采用[[java命名规则|大驼峰命名法]]

类型命名规则示例
类名大驼峰命名法PersonInfo
接口名大驼峰命名法DataService
方法名小驼峰命名法getUserInfo()
变量名小驼峰命名法userAge
常量全大写 + 下划线MAX_VALUE

众所周知,面向对象有三大特性:封装、继承、多态,下面我们就来讲解面向对象吧!

1. 类(Class)

  • 定义: 类是一种 模板或蓝图,它定义了一类对象共有的属性(成员变量)和行为(成员方法)。类本身并不占用内存,只有在创建对象时才会分配内存。

  • 作用:

    • 组织代码: 将相关的数据和方法封装在一起,形成一个独立的模块。

    • 抽象现实: 用类来描述现实世界中的事物,将其共同的特性抽取出来。

  • 语法示例:

     public class Person {
         // <span style="color: red;">属性</span>
         private String name;
         private int age;
     ​
         // <span style="color: red;">构造方法</span>:用于初始化对象
         public Person(String name, int age) {
             this.name = name;
             this.age = age;
         }
     ​
         // <span style="color: red;">方法</span>:定义对象的行为
         public void speak() {
             System.out.println("Hello, my name is " + name);
         }
     }

  • 重点总结:

    • 类是 抽象描述,而不是具体存在的实例。

    • 它定义了对象的 属性行为


2. 对象(Object)

  • 定义: 对象是类的 具体实例。当你根据一个类创建对象时,就在内存中为这个对象分配了存储空间,并且这个对象拥有类中定义的属性和方法。

  • 作用:

    • 数据存储: 对象拥有自己独立的属性值,即使是同一个类的不同对象,其状态也可以不同。

    • 行为执行: 对象可以调用类中定义的方法,从而实现特定的功能。

  • 创建对象的语法:

     
    public class Main {
         public static void main(String[] args) {
             // <span style="color: red;">创建对象</span>:使用 new 关键字调用构造方法
             Person person = new Person("Alice", 25);
             person.speak();  // 输出:Hello, my name is Alice
         }
     }

  • 重点总结:

    • 对象是类的 实例化,在内存中存在具体数据。

    • 每个对象都有自己的 状态(属性值)和 行为(方法)。


3. 类与对象的关系

  • 类是模板,对象是实例 一个类可以创建多个对象,每个对象都根据同一个模板构建,但它们的数据可以不同。

    重点: 类提供了抽象和定义,对象则是真正参与运算和数据存储的实体。

  • 内存分配: 类的定义本身不会占用太多内存,而对象创建时会在堆内存中分配空间存储属性数据。

  • 行为调用: 通过对象调用类中定义的方法,实现对数据的操作和功能的执行。

4. 类的基本语法

1. 构造器(Constructor)
  • 定义: 构造器是 用于创建和初始化对象的特殊方法。 它的名称必须与类名相同,并且没有返回值。

  • 特点:

    • 自动调用: 每次使用 new 创建对象时,都会自动调用构造器。

    • 重载: 可以根据需要定义多个构造器(构造器重载),以支持不同的初始化方式。

    • 无参构造器: 如果没有显式定义构造器,编译器会自动提供一个默认的无参构造器。

  • 示例:

     
    public class Person {
         private String name;
         private int age;
     ​
         // 无参构造器
         public Person() {
             this.name = "Unknown";
             this.age = 0;
         }
     ​
         // 带参构造器
         public Person(String name, int age) {
             this.name = name;
             this.age = age;
         }
     }

  • 类默认就自带了一个无参构造器

  • 如果为类定义了有参数构造器,类默认的无参数构造器就没有了,此时如果还想用无参数构造器,就必须自己手写一个无参数构造器出来。


2. this 关键字
  • 定义: this 是一个引用变量,指向当前对象的引用。

  • 用途:

    • 区分成员变量与局部变量: 当成员变量与局部变量同名时,使用 this 可以明确指向当前对象的成员变量。

    • 调用构造器: 可用 this(...) 调用本类中其它构造器,避免代码重复。

    • 返回当前对象: 在链式调用中,经常用 this 返回当前对象。

  • 示例:

     public class Person {
         private String name;
         private int age;
     ​
         public Person(String name, int age) {
             // 使用 this 解决局部变量和成员变量同名问题
             this.name = name;
             this.age = age;
         }
     ​
         public Person() {
             // 调用带参构造器
             this("Unknown", 0);
         }
         public printThis
     }


3. 封装(Encapsulation)
  • 定义: 封装是 将数据(属性)和操作数据的方法组合在一起,并对外隐藏实现细节 的一种技术。

  • 实现方式:

    • 访问修饰符: 使用 privateprotectedpublic 控制对类内部成员的访问。

    • Getter 和 Setter 方法: 通过公开的方法来访问和修改私有属性,保证数据安全性和合法性。

  • 优点:

    • 数据保护: 限制外部直接访问和修改对象状态。

    • 提高代码维护性: 内部实现可随时修改,而不影响外部调用者。

  • 示例:

     public class Student {
         // 私有属性,外部不可直接访问
         private String name;
         private int age;
     ​
         // Getter 方法
         public String getName() {
             return name;
         }
     ​
         // Setter 方法
         public void setName(String name) {
             this.name = name;
         }
     ​
         public int getAge() {
             return age;
         }
     ​
         public void setAge(int age) {
             if(age >= 0) { // 合法性检查
                 this.age = age;
             }
         }
     }

    使用私有关键字(private),来隐藏成员变量,使用公开的pubilc修饰的get和set方法来合理暴露成员变量的取值和赋值!


4. Javabean
  • 定义: JavaBean 是一种遵循特定 编写规范的 Java 类,用于封装多个对象属性,便于组件化开发和数据传输。(可暂且看做是[[Javabean和实体类|实体类]])

  • 规范要求:

    • 类必须有一个 无参构造器

    • 类的属性通常使用私有访问修饰符,并提供 public 的 Getter/Setter 方法

    • 实现序列化接口(Serializable),以支持对象的序列化(可选,但在某些场景下要求)。

  • 示例:

     import java.io.Serializable;
     ​
     public class Employee implements Serializable {
         private String id;
         private String name;
     ​
         // 无参构造器
         public Employee() {
         }
     ​
         // Getter 和 Setter 方法
         public String getId() {
             return id;
         }
     ​
         public void setId(String id) {
             this.id = id;
         }
     ​
         public String getName() {
             return name;
         }
     ​
         public void setName(String name) {
             this.name = name;
         }
     }


5. static 关键字
  • 定义: static 用于声明类级别的成员(变量或方法),这些成员不依赖于任何对象的实例,而属于整个类。

  • 用途:

    • 静态变量: 类变量,在所有对象之间共享同一份数据。

    • 静态方法: 可以在不创建对象的情况下调用,通常用于工具类或与对象状态无关的操作。

    • 静态代码块: 用于类加载时进行一次性的初始化操作。

  • 注意事项:

    • 静态方法中不能直接访问非静态(实例)成员。

    • 静态成员在内存中只有一份,适合保存常量或全局状态。 总结

特性说明
属于类static 方法属于类,而不是对象。
访问限制只能访问 static 变量和 static 方法,不能访问实例变量和实例方法。
不能使用 thissuper因为 static 方法不属于对象。
适合作为工具方法适用于无状态、通用的功能,如 Math.pow()Arrays.sort()
不能被重写,但可以隐藏静态方法在子类中不会覆盖父类方法,而是隐藏。
main 方法必须是 staticJVM 需要在不创建对象的情况下调用 main 方法。
  • 示例:

     public class MathUtil {
         // 静态变量:常量
         public static final double PI = 3.14159;
     ​
         // 静态方法:工具方法
         public static int add(int a, int b) {
             return a + b;
         }
     ​
         // 静态代码块:类加载时执行
         static {
             System.out.println("MathUtil 类加载...");
         }
     }
     ​
     // 调用方式:
     // MathUtil.PI
     // MathUtil.add(3, 5);
    如果在对象中,我们可以直接用类名来调用静态方法
    
     //学生类
     public class Student {  
         private double score;
         // 静态方法:有static修饰,属于类持有。
         public static void printHelloWorld(){
             System.out.println("Hello World");
             System.out.println("Hello World");
             System.out.println("Hello World");
         }  
         // 实例方法:没有static修饰,属于对象持有。
         public void printPass(){
             System.out.println(score >= 60 ? "通过了" : "您挂科了!");
         }
         public void setScore(double score) {
             this.score = score;
         }
     }  
     ​
     //测试类
     public class Test {
     ​
         public static void main(String[] args) {
     ​
             // 目标:认识static修饰和不修饰方法的区别
             
             // 1、类名.静态方法(推荐)
             Student.printHelloWorld();
     ​
             // 2、对象.静态方法(不推荐)
             Student s1 = new Student();
             s1.printHelloWorld();
     ​
     ​
             // 3、对象名.实例方法
             // Student.printPass(); // 报错
             s1.setScore(59.5);
     ​
             s1.printPass();
             // 规范:如果这个方法只是为了做一个功能且不需要直接访问对象的数据,这个方法直接定义成静态方法
             //     如果这个方法是对象的行为,需要访问对象的数据,这个方法必须定义成实例方法
             // Test.printHelloWorld2();
     ​
             printHelloWorld2();
         }
         public static void printHelloWorld2(){
             System.out.println("Hello World");
             System.out.println("Hello World");
             System.out.println("Hello World");
             System.out.println("Hello World");
         }
     }


总结
  • 构造器:用于初始化对象,没有返回值,并且可以重载。

  • this:引用当前对象,用于区分成员变量和局部变量,以及调用其他构造器。

  • 封装:隐藏对象的内部实现细节,通过访问修饰符和 Getter/Setter 实现。

  • Javabean:遵循特定规范的 Java 类,便于组件化开发,通常具备无参构造器和 Getter/Setter 方法。

  • static:声明类级别成员,不依赖于对象实例,可用于常量、工具方法等场景.

掌握这些概念有助于编写结构清晰、易维护且高内聚低耦合的 Java 程序。

5.final

  1. 认识final

final关键字是最终的意思

  • 修饰类:表示该类为最终类,意思是该类不能被继承了。

  • 修饰方法:表示该方法为最终方法,不能被重写。

  • 修饰变量:表示变量不能被修改,只能被赋值一次。

PS: **final修饰基本类型的变量,变量存储的数据不能被改变。 final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。

  1. 常量

  • 使用了 static final 修饰的成员变量就被称为常量.

  • 作用:常用于记录系统的配置信息。 注意:常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。 优势:1. 代码的可读性更好,维护性也更好。 2.程序编译后,会被宏替换,出现常量的地方,会被替换成字面量,这和字面量的性质是一样的

6.继承

1.认识继承

了解继承之前我们先来了解一下为什么要有继承,我们来看下面这段代码 可以看到这段代码有很多重复的部分,这部分代码的复用性不高,所以我们可以把相同的代码用一个父类来表示,用子类继承父类的代码,这样我们就可以实现代码的复用 !

2.权限修饰符

private:z只能在本类中访问(改变参数,可以提供get和set方法) 缺省:本类,和同一个包中的类 [[protected在多态中的访问权限|protected]]::本类同一个包中的类,子孙类(只要被继承的类都可以🧐) pubic:全局都可以访问 

3.继承的特点

1.单继承

继承中的类为什么不支持多继承呢?

假设如果有一个子类继承了两个父类,那两个父类中又有相同的方法,这时候去调用就会乱套

2.就近原则
  1. 在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。

  • 先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果父类范围还没有找到则报错。

  1. 如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的怎么办?

  • 可以通过super关键字,指定访问父类的成员:

    super.父类成员变量/父类成员方法

4.方法重写

  • 当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。

  • 重写小技巧:使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。

方法重写的其它注意事项

  • 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限( public > protected > 缺省 )。

  • 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。

  • 私有方法、静态方法不能被重写,如果重写会报错的。

5.子类构造器的特点

  1. 子类构造器都会先调用父类构造器,在调用自己的

  2. 子类构造器都会调用super()方法(写不写都有),会自动调用父类的无参构造器

  3. 如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(….),指定去调用父类的有参数构造器。

// 父类  
// 继承的好处:1.代码复用 2.减少了重复代码。  
public class People {  
    private String name;  
    private char sex;  
  
    public People() {  
    }  
  
    public People(String name, char sex) {  
        this.name = name;  
        this.sex = sex;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public char getSex() {  
        return sex;  
    }  
  
    public void setSex(char sex) {  
        this.sex = sex;  
    }  
}
package com.itheima.extends6constructor;  
  
// 子类  
public class Teacher extends People {  
    private String skill; // 技术。  
  
    public Teacher() {
    //自动调用无参构造器super();
    }  
  
    public Teacher(String name, String skill, char sex) {  
        // 子类构造器调用父类构造器的应用场景。  
        // 可以把子类继承自父类有参构造器的参数 
        super(name, sex);  
        this.skill = skill;  
    }  
  
    public String getSkill() {  
        return skill;  
    }  
  
    public void setSkill(String skill) {  
        this.skill = skill;  
    }  
}

//测试类
public class Test{
	public static void main(String[] args) {
		// 目标:子类构造器调用父类构造器的应用场景。  
		Teacher t = new Teacher("dlei", "java、大数据、微服务", '男');  
		System.out.println(t.getName());  
		System.out.println(t.getSkill());  
		System.out.println(t.getSex());
	}
}

补充:可以通过this()去调用兄弟构造器 ps:在super不能再static方法中调用super

class Parent {
    static void display() {
        System.out.println("Parent static 方法");
    }
}

class Child extends Parent {
    static void display() {
        // super.display(); // ❌ 错误,static 方法不能使用 super
        System.out.println("Child static 方法");
    }
}

7.多态

1.认识多态

什么是多态?

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

  • 多态的具体代码体现 

多态的前提

  • 有继承/实现关系;存在父类引用子类对象;存在方法重写。

多态的一个注意事项

  • 多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。

 

2.多态的好处

使用多态的好处

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

  • 定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。

多态下会产生的一个问题,怎么解决?

  • 多态下不能使用子类的独有功能。(子类中有,父类中没有的方法)

public class Animal {

    String name = "动物";

    public void run(){

        System.out.println("动物会跑~~~");

    }

}

//继承了动物类
public class Tortoise extends Animal{

    String name = "乌龟";

    @Override

    public void run() {

        System.out.println("🐢跑的贼慢~~~");

    }

}

public class Wolf extends Animal{

    String name = "狼";

  

    @Override

    public void run() {

        System.out.println("🐺跑的贼溜~~~");

    }

}

public class Test {

    public static void main(String[] args) {

 
        // 1、对象多态、行为多态。

        Animal a1 = new Wolf();

        a1.run(); // 方法:编译看左边,运行看右边
        //结果:🐺跑的贼溜~~~

        System.out.println(a1.name); // 成员变量:编译看左边,运行也看左边

  

        Animal a2 = new Tortoise();

        a2.run(); // 方法:编译看左边,运行看右边
        //结果:🐢跑的贼慢~~~

        System.out.println(a2.name); // 成员变量:编译看左边,运行也看左边

    }

}

^9eed78

注意:多态·的权限访问也看左边,例如:使用[[protected在多态中的访问权限|protected]]访问限定符时,虽然重写了子类的方法,但是,权限的检查是在编译的时候进行的。所以,虽然protected规定子孙类可以访问,但是利用多态的性质编译的时候是在左边父类检查权限限定符

3.多态下的类型转换

//动物类、狼类、乌龟类在上方[[#^314268]]

public class Test {

    public static void main(String[] args) {

        // 目标:认识多态的代码。

        // 1、多态的好处1: 右边对象是解耦合的。

        Animal a1 = new Tortoise();

        a1.run();

        // a1.shrinkHead(); // 多态下的一个问题:多态下不能调用子类独有功能。

  

        // 强制类型转换:可以解决多态下调用子类独有功能

        Tortoise t1 = (Tortoise) a1;

        t1.shrinkHead();

  

        // 有继承关系就可以强制转换,编译阶段不会报错!

        // 运行时可能会出现类型转换异常:ClassCastException

        // Wolf w1 = (Wolf) a1;

  

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

  

        Wolf w = new Wolf();

        go(w);

  

        Tortoise t = new Tortoise();

        go(t);

    }

  

    public static void go(Animal a){

        System.out.println("开始。。。。。");

        a.run();

        // a1.shrinkHead(); // 报错,多态下不能调用子类独有功能。

        // java建议强制转换前,应该判断对象的真实类型,再进行强制类型转换。

        if(a instanceof Wolf){

            Wolf w1 = (Wolf) a;

            w1.eatSheep();

        }else if(a instanceof Tortoise){

            Tortoise t1 = (Tortoise) a;

            t1.shrinkHead();

        }

    }

}

ps:instanceof 是 JavaScript(以及 Java、PHP 等语言)中的一个运算符,用于检查一个对象是否是某个构造函数(类)的实例。

8.单例类

了解单例类之前,先来了解一下设计模式!

  • Java 中的[[Java中的设计模式|设计模式]](Design Patterns)是一套被广泛认可的代码编写最佳实践,它们提供了解决特定软件设计问题的通用方法。设计模式可以帮助程序员编写更高效、可维护、可扩展的代码。

  • 通常有三种类型创建型、行为型、结构型。

  • 设计模式不是必须使用的规则,而是提高代码质量的经验总结。学习设计模式可以帮助你编写更易维护、可扩展的 Java 代码,在实际开发中灵活运用。 

1.饿汉式单例类
// 饿汉式单例类

public class A {

    // 2、定义一个静态变量,用于存储本类的一个唯一对象。

    // public static final A a = new A();

    private static A a = new A();

  

    // 1、私有化构造器: 确保单例类对外不能创建太多对象,单例才有可能性。

    private A() {

    }

  

    // 3、提供一个公开的静态方法,返回这个类的唯一对象。

    public static A getInstance() {

        return a;

    }

}
2.懒汉式单例类
// 懒汉式单例类。

public class B {

    // 2、私有化静态变量

    private static B b;

  

    // 1、私有化构造器

    private B() {

    }

  

    // 3、提供静态方法返回对象: 真正需要对象的时候才开始创建对象。

    public static B getInstance() {

        if (b == null) {

            // 第一次拿对象时,会创建对象,给静态变量b记住。

            b = new B();

        }

        return b;

    }

}

9.枚举类

枚举类是一种特殊的类

  • 枚举类中的第一行,只能写枚举类的对象名称,且要用逗号隔开。

  • 这些名称,本质是常量,每个常量都记住了枚举类的一个对象。

特点

  • 枚举都是最终类,不可以被继承,枚举类都是继承java.lang.Enum类的。

  • 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量会记住枚举类的一个对象。

  • 枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。

  • 编译器为枚举类新增了几个方法。

  • 枚举类适合做信息的分类与标识 ps:枚举上下左右

public class Constant {

    public static final int UP = 0; // 上

    public static final int DOWN = 1; // 下

    public static final int LEFT = 2; // 左

    public static final int RIGHT = 3; // 右

}



public class Test2 {

    public static void main(String[] args) {

        // 目标:掌握枚举类的应用场景:做信息的分类和标志。

        // 需求:模拟上下左右移动图片。

        // 第一种是常量做信息标志和分类: 但参数值不受约束。

        move(Constant.UP);

        // 第二种是枚举做信息标志和分类: 参数值受枚举类约束。

        move2(Direction.DOWN);

    }

  

    public static void move2(Direction direction){

        // 根据这个方向做移动:上下左右。

        switch (direction){

            case UP :

                System.out.println("向上移动");

                break;

            case DOWN :
                System.out.println("向下移动");
                break;
            case LEFT :
               System.out.println("向左移动");
                break;
            case RIGHT :
                System.out.println("向右移动");
                break;
        }
    }

    public static void move(int direction){

        // 根据这个方向做移动:上下左右。
        switch (direction){
            case Constant.UP :
                System.out.println("向上移动");
                break;
            case Constant.DOWN :
                System.out.println("向下移动");
                break;
            case Constant.LEFT :
                System.out.println("向左移动");
                break;

            case Constant.RIGHT :
                System.out.println("向右移动");
                break;
            default:
                System.out.println("输入有误");

        }

    }

}

10.抽象类

  • 在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法。

  • abstract修饰类,这个类就是抽象类。

  • abstract修饰方法,这个方法就是抽象方法。

修饰符 abstract class 类名{

  修饰符 abstract 返回值类型 方法名称(形参列表);

 }
// 抽象方法:必须abstract修饰,只有方法签名,不能有方法体
public abstract class A { 
	public abstract void test();  
}

  • 抽象类中不一定要有抽象方法,有抽象方法的类必须是抽象类。

  • 类有的成员:成员变量、方法、构造器,抽象类都可以有。

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

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

11.接口

接口的好处

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

  • 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现(更利于程序的解耦合)。

public interface A {

    // JDK 8之前,接口中只能定义常量和抽象方法。

    // 1、常量:接口中定义常量可以省略public static final不写,默认会加上去。

    String SCHOOL_NAME = "黑马程序员";

    // public static final String SCHOOL_NAME2 = "黑马程序员";

  

    // 2、抽象方法: 接口中定义抽象方法可以省略public abstract不写,默认会加上去。

    // public abstract void run();

    void run();

    String go();
 }
 
public interface B {

    void play(); // 玩

}

// C被称为实现类。同时实现了多个接口。

// 实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则这个类必须定义成抽象类

class C implements  B , A{
    @Override

    public void run() {

        System.out.println("C类重写了run方法");

    }
    @Override

    public String go() {

        return "黑马找磊哥!";
 }
    @Override

    public void play() {

        System.out.println("C类重写了play方法");
    }
}

从JDK8开始,接口interface增加了三种方法

public interface A{    
	/**  
     * 1、默认方法(实例方法):使用用default修饰,默认会被加上public修饰。     * 注意:只能使用接口的实现类对象调用     
     **/    
     default void test1(){  
        ...  
    }  
      /**  
     * 2、私有方法:必须用private修饰(JDK 9开始才支持)  
     **/    
     private void test2(){  
        ...  
    }    
    /**  
     * 3、类方法(静态方法):使用static修饰,默认会被加上public修饰。     * 注意:只能用接口名来调用。 
     **/    
     static void test3(){  
        ...  
    }  
}
  • 默认方法:使用default修饰,使用实现类的对象调用。

  • 静态方法:static修饰,必须用当前接口名调用

  • 私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。

  • 他们都会默认被public修饰。

注意事项

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

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

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

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

13.代码块

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

  • 静态代码块:

Ø 格式:static { }

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

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

public class CodeDemo1 {
    public static String schoolName;
    public static String[] cards = new String[54];
    // 静态代码块:有static修饰,属于类,与类一起优先加载,自动执行一次
    // 基本作用:可以完成对类的静态资源的初始化
    static {
        System.out.println("===静态代码块执行了====");
        schoolName = "黑马程序员";
        cards[0] = "A";
        cards[1] = "2";
        cards[2] = "3";
        // ...
    }

    public static void main(String[] args) {

        // 目标: 认识代码块,搞清楚代码块的基本作用。

        System.out.println("===main方法执行了====");

        System.out.println(schoolName);

        System.out.println(Arrays.toString(cards)); // 返回数组的内容观察

    }

}
  • 实例代码块:

Ø  格式:{ }

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

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

public class CodeDemo2 {
    private String name;
    private String[] direction = new String[4]; // 实例变量
    // 实例代码块:无static修饰。属于对象,每次创建对象时,都会优先执行一次。
    // 基本作用:初始化对象的实例资源。
        System.out.println("=========实例代码块执行了=========");
        name = "itheima"; // 赋值
        direction[0] = "N";
        direction[1] = "S";
        direction[2] = "E";
        direction[3] = "W";
    }
    public static void main(String[] args) {
        // 目标:实例代码块。
        System.out.println("=========main方法执行了=========");
        new CodeDemo2();
        new CodeDemo2();
        new CodeDemo2();
    }
}

14.内部类

  • 如果一个类定义在另一个类的内部,这个类就是内部类。

  • 场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。

1.成员内部类
  • 就是类中的一个普通成员,类似前面我们学过的普通的成员变量、成员方法。

public class Outer {    
	// 成员内部类   
	public class Inner {        

    }  
}

成员内部类中访问其他成员的特点 1、成员内部类种可以直接访问外部类的实例成员、静态成员。 2、成员内部类的实例方法中,可以直接拿到当前外部类对象,格式是:外部类名.this 。

public class InnerClassDemo1 {
    public static void main(String[] args) {
        // 目标:搞清楚成员内部类的语法。
        // 成员内部类创建对象的格式:
        // 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
        Outer.Inner oi = new Outer().new Inner();
        oi.setName("王麻子");
        oi.show();
        People.Heart heart = new People().new Heart();
        heart.show();
    }
}
class People {
    private int heartBeat = 100;
    public class Heart {
        private int heartBeat = 80;
        public void show() {
            int heartBeat = 200;
            System.out.println(heartBeat);// 200
            System.out.println(this.heartBeat);// 80
            System.out.println(People.this.heartBeat);// 100
        }
    }
}
2.静态内部类

有static修饰的内部类,属于外部类自己持有

外部类名.内部类名 对象名 = new 外部类.内部类(…); Outer.Inner in =  new Outer.Inner();

特点

  • 有static修饰的内部类。

  • 外部类名.内部类名 对象名 = new 外部类.内部类(…);

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

3.局部内部类

4.🔔匿名内部类(重点)

  • 是一种特殊的局部内部类;

  • 所谓匿名:指的是程序员不需要为这个类声明名字,默认有个隐藏的名字。

new  类或接口(参数值…) {      类体(一般是方法重写); };

特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象。 作用:用于更方便的创建一个子类对象。

public abstract class Animal {
    public abstract void cry();
}
public class Test {
    public static void main(String[] args) {
        // 目标:认识匿名内部类,搞清楚其基本作用。
        // 匿名内部类实际上是有名字:外部类名$编号.class
        // 匿名内部类本质是一个子类,同时会立即构建一个子类对象
        Animal a = new Animal(){
            @Override
            public void cry() {
                System.out.println("🐱猫是喵喵喵的叫~~~~");
            }
        };
        a.cry();
    }

}

匿名内部类的真实使用场景

public class Test {
    public static void main(String[] args) {
       // 目标:搞清楚匿名内部类的使用形式(语法): 通常可以做为一个对象参数传输给方法使用。
        // 需求:学生,老师都要参加游泳比赛。
        Swim s1 = new Swim() {
            @Override
            public void swimming() {
                System.out.println("学生🏊‍贼快~~~~");
            }
        };
        start(s1);
          System.out.println("====================================");

        start(new Swim() {
            @Override
            public void swimming() {
                System.out.println("老师🏊‍贼溜~~~~");
            }
        });
    }
  
    // 设计一个方法,可以接收老师,和学生开始比赛。
    public static void start(Swim s) {
        System.out.println("开始。。。。");
        s.swimming();
        System.out.println("结束。。。。");
    }
}

interface Swim {

    void swimming(); // 游泳方法

}

调用别人提供的方法实现需求时,这个方法正好可以让我们传输一个匿名内部类对象给其使用。

15.函数式编程

  • 此“函数”类似于数学中的函数(强调做什么),只要输入的数据一致返回的结果也是一致的

  • 使用Lambda函数替代某些匿名内部类对象,从而让程序代码更简洁,可读性更好。

public class LambdaDemo1 {
    public static void main(String[] args) {
        // 目标:认识Lambda表达式:搞清楚其基本作用。
        Animal a = new Animal() {
            @Override
            public void cry() {
                System.out.println("🐱是喵喵喵的叫~~~~");
            }
        };
        a.cry();
        //错误示范:Lambda并不是可以简化全部的匿名内部类,Lambda只能简化函数式接口的匿名内部类。
//        Animal a1 = () -> {
//            System.out.println("🐱是喵喵喵的叫~~~~");
//        };
//        a1.cry();
      System.out.println("=====================================================");
  
       /* Swim s1 = new Swim() {
            @Override
            public void swimming() {
                System.out.println("学生🏊‍贼快~~~~");
            }
        }; */

        // Lambda只能简化函数式接口的匿名内部类。
        Swim s1 = () -> {
            System.out.println("学生🏊‍贼快~~~~");
        };
        s1.swimming();
    }
} 
abstract class Animal{
    public abstract void cry();
}

16.lambada表达式

Lambda表达式的简化流程主要围绕减少冗余代码展开,通过逐步省略不必要的语法元素来实现。以下是典型的简化步骤和规则:


1. 从匿名内部类到Lambda的初始转换

原始匿名内部类:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

第一步Lambda转换:

Runnable r = () -> { 
    System.out.println("Hello");
};

关键变化:

  • 删除接口名和函数名(编译器通过函数式接口推断)

  • -> 符号替代方法结构


2. 参数类型的省略

当参数类型可以被编译器推断时,可省略显式类型声明:

// 原始带类型声明
Comparator<String> c = (String a, String b) -> a.compareTo(b);

// 省略类型(编译器自动推断为String)
Comparator<String> c = (a, b) -> a.compareTo(b);

3. 单参数的括号省略

当只有一个参数时,可省略参数括号:

// 原始带括号
Consumer<String> c = (s) -> System.out.println(s);

// 省略括号
Consumer<String> c = s -> System.out.println(s);

4. 单行代码的大括号和分号省略

当Lambda体只有单条语句时:

  • 可省略 {};

  • 隐含 return(如果有返回值)

// 原始带大括号
Function<Integer, Integer> f = (x) -> { return x * x; };

// 简化后(省略括号、分号和return)
Function<Integer, Integer> f = x -> x * x;

5. 多参数但无操作的参数忽略

若参数未被使用,可用 _ 占位(Java 8不支持,Java 9+部分支持):

// 假设一个接口方法接受两个参数但只用第一个
BiConsumer<Integer, Integer> c = (a, _) -> System.out.println(a);

6. 方法引用的进一步简化

当Lambda仅调用已有方法时,可替换为方法引用:

// Lambda表达式
Consumer<String> c = s -> System.out.println(s);

// 方法引用简化
Consumer<String> c = System.out::println;

简化规则总结表

简化条件可省略元素示例
单参数参数括号 ()s -> ...
可推断类型参数类型声明(a, b) -> ...
单行代码大括号 {} 和分号 ;x -> x * x
仅返回单行值return 关键字(a, b) -> a + b
直接调用现有方法Lambda体 → 方法引用System.out::println

简化流程示例

原始匿名类:

List<String> list = Arrays.asList("a", "b");
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String String b) {
        return a.compareTo(b);
    }
});

逐步简化:

  1. 转换为Lambda:

    Collections.sort(list, (String a, String b) -> { return a.compareTo(b); });
  2. 省略参数类型:

    Collections.sort(list, (a, b) -> { return a.compareTo(b); });
  3. 省略大括号和return

    Collections.sort(list, (a, b) -> a.compareTo(b));
  4. 方法引用最终简化:

    Collections.sort(list, String::compareTo);

关键原理
  • 函数式接口:Lambda依赖只有一个抽象方法的接口(如 Runnable, Comparator)。

  • 类型推断:编译器通过上下文自动推断参数类型和返回类型。

  • 简洁性优先:设计目标是减少模板代码,提升可读性。

通过以上步骤,可以逐步将冗长的匿名内部类代码简化为高度紧凑的Lambda表达式或方法引用。

lambada表达式的简化案例

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("登录成功!");
            }
        });
  
        btn.addActionListener((ActionEvent e) -> {
                System.out.println("登录成功!");
        });
        
       btn.addActionListener((e) -> {
            System.out.println("登录成功!");
        });
        
        btn.addActionListener(e -> {
            System.out.println("登录成功!");
        });
        btn.addActionListener(e ->  System.out.println("登录成功!"));
        win.setVisible(true);
 // 需求:按钮年龄升序排序。可以调用sun公司写好的API直接对数组进行排序。
         Arrays.sort(students, new Comparator<Student>() {
             @Override
             public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge(); // 按照年龄升序!
             }
         })
 ​
         Arrays.sort(students, (Student o1, Student o2) -> {
             return o1.getAge() - o2.getAge(); // 按照年龄升序!
         })
         
         Arrays.sort(students, (o1,  o2) -> {
             return o1.getAge() - o2.getAge(); // 按照年龄升序!
         });
         
         Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值